diff --git a/devU-client/src/components/pages/assignments/assignmentDetailPage.tsx b/devU-client/src/components/pages/assignments/assignmentDetailPage.tsx index 95f0614..3c9b1b0 100644 --- a/devU-client/src/components/pages/assignments/assignmentDetailPage.tsx +++ b/devU-client/src/components/pages/assignments/assignmentDetailPage.tsx @@ -8,15 +8,19 @@ import LoadingOverlay from 'components/shared/loaders/loadingOverlay' import {useActionless, useAppSelector} from 'redux/hooks' import {SET_ALERT} from 'redux/types/active.types' -//import Card from '@mui/material/Card' -//import CardContent from '@mui/material/CardContent' -import {/*Accordion AccordionDetails, AccordionSummary*/ TextField, Typography} from '@mui/material' +import Card from '@mui/material/Card' +import CardContent from '@mui/material/CardContent' +import {Accordion, AccordionDetails, CardActionArea, TextField, Typography} from '@mui/material' + import Grid from '@mui/material/Unstable_Grid2' import styles from './assignmentDetailPage.scss' import {prettyPrintDateTime} from "../../../utils/date.utils"; +import { useLocation } from 'react-router-dom'; +import Scoreboard from '../assignments/scoreboard'; + const AssignmentDetailPage = () => { const [setAlert] = useActionless(SET_ALERT) const history = useHistory() @@ -37,10 +41,12 @@ const AssignmentDetailPage = () => { // const [containerAutograder, setContainerAutograder] = useState() // const containerAutograder = false; //TODO: Use the above commented out code to get the container autograder // const [ setNonContainerAutograders] = useState(new Array ()) + const [showScoreboard, setShowScoreboard] = useState(false); + const location = useLocation(); useEffect(() => { fetchData() - }, []); + }, [location]); const fetchData = async () => { @@ -90,10 +96,13 @@ const AssignmentDetailPage = () => { const key = e.target.id setFormData(prevState => ({...prevState,[key] : e.target.value})) } + + const handleFileChange = (e : React.ChangeEvent) => { setFile(e.target.files?.item(0)) } + const handleSubmit = async () => { let response; const contentField = { @@ -122,7 +131,7 @@ const AssignmentDetailPage = () => { } else { response = await RequestService.post(`/api/course/${courseId}/assignment/${assignmentId}/submissions`, submission); } - + setAlert({ autoDelete: true, type: 'success', message: 'Submission Sent' }) // Now you can use submissionResponse.id here @@ -139,14 +148,15 @@ const AssignmentDetailPage = () => { } } + return(

{assignment?.name}

-
+
- + {role.isInstructor() && ( <>
@@ -175,16 +185,22 @@ const AssignmentDetailPage = () => { (`/course/${courseId}/assignment/${assignmentId}/submissions`) }}>Grade Submissions} +
+ {role.isInstructor() && + } + + +
)} - - - - - +
@@ -196,26 +212,24 @@ const AssignmentDetailPage = () => { {`Due Date: ${new Date(assignment.dueDate).toLocaleDateString()}`} )} {assignmentProblems && assignmentProblems.length > 0 ? ( - assignmentProblems.map((assignment, index) => ( -
-
-
{`Assignment Problem ${index + 1}`}
-
-
- {assignment.problemName} - -
-
+ assignmentProblems.map((assignmentProblem, index) => ( + + + + {assignmentProblem.problemName} + + + )) - + ) : (
No Problems Exist
)} - + {!(assignment?.disableHandins) && ()} - + {assignmentProblems && assignmentProblems.length > 0 ? (
@@ -230,7 +244,7 @@ const AssignmentDetailPage = () => {
- {/**Submissions List */} +
{submissions.map((submission, index) => ( @@ -245,9 +259,16 @@ const AssignmentDetailPage = () => {
))} + + {showScoreboard && ( +
+ + +
+ )}
- +
) } diff --git a/devU-client/src/components/pages/assignments/scoreboard.scss b/devU-client/src/components/pages/assignments/scoreboard.scss new file mode 100644 index 0000000..2de1f3e --- /dev/null +++ b/devU-client/src/components/pages/assignments/scoreboard.scss @@ -0,0 +1,24 @@ +@import 'variables'; + +.scoreboardTable { + width: 100%; + border-collapse: collapse; + margin-top: 20px; + + th, td { + border: 1px solid #ddd; + padding: 8px; + text-align: left; + } + + th { + background-color: rebeccapurple; + + + } + .scoreboardContainer { + margin-top: 20px; + border: 1px solid #ddd; + padding: 5px; + } +} \ No newline at end of file diff --git a/devU-client/src/components/pages/assignments/scoreboard.tsx b/devU-client/src/components/pages/assignments/scoreboard.tsx new file mode 100644 index 0000000..3c794cc --- /dev/null +++ b/devU-client/src/components/pages/assignments/scoreboard.tsx @@ -0,0 +1,46 @@ + +import React from 'react'; +import styles from './scoreboard.scss'; + +interface ScoreboardProps { + courseId: string; + assignmentId: string; +} + +const Scoreboard: React.FC = ({ courseId, assignmentId }) => { + // Dummy scoreboard data + const scoreboardData = [ + { UBit: 'ashwa', score: 95, runtime: '1.2s' }, + { UBit: 'yessicaq', score: 88, runtime: '1.5s' }, + { UBit: 'neemo', score: 76, runtime: '2.1s' }, + { UBit: 'alex', score: 29, runtime: '17.2s' }, + { UBit: 'jesse', score: 56, runtime: '9.5s' }, + { UBit: 'kevin', score: 7, runtime: '100s' }, + + ]; + + return ( +
+

{`Scoreboard for Assignment ${assignmentId} in Course ${courseId}`}

+ + + + + + + + + + {scoreboardData.map((entry) => ( + + + + + + ))} + +
UBitScoreRuntime
{entry.UBit}{entry.score}{entry.runtime}
+
+ ); +}; +export default Scoreboard; diff --git a/devU-client/src/components/pages/courses/courseDetailPage.scss b/devU-client/src/components/pages/courses/courseDetailPage.scss index 9f647cf..709a7b7 100644 --- a/devU-client/src/components/pages/courses/courseDetailPage.scss +++ b/devU-client/src/components/pages/courses/courseDetailPage.scss @@ -134,6 +134,16 @@ color: white !important; } +.assignmentName { + &::after { + content: ''; + display: block; + width: 100%; + margin: 5px auto; + border-bottom: 1px solid #ccc; + } +} + diff --git a/devU-client/src/components/pages/courses/courseDetailPage.tsx b/devU-client/src/components/pages/courses/courseDetailPage.tsx index 3802b67..03e261a 100644 --- a/devU-client/src/components/pages/courses/courseDetailPage.tsx +++ b/devU-client/src/components/pages/courses/courseDetailPage.tsx @@ -103,7 +103,7 @@ const CourseDetailPage = () => {

Instructor:

-
+
@@ -168,6 +168,7 @@ const CourseDetailPage = () => { history.push(`/course/${courseId}/assignment/${assignment.id}`) }}> {assignment.name} @@ -176,14 +177,14 @@ const CourseDetailPage = () => { secondary={ - Start Date: {new Date(assignment.startDate).toLocaleDateString()} -
{/* Add a line break */} - Due Date: {new Date(assignment.dueDate).toLocaleDateString()} + Start: {new Date(assignment.startDate).toLocaleDateString()} + | + Due: {new Date(assignment.dueDate).toLocaleDateString()}
} diff --git a/devU-client/src/components/pages/forms/containers/nonContainerAutoGraderForm.tsx b/devU-client/src/components/pages/forms/containers/nonContainerAutoGraderForm.tsx index cd6d5e6..7247126 100644 --- a/devU-client/src/components/pages/forms/containers/nonContainerAutoGraderForm.tsx +++ b/devU-client/src/components/pages/forms/containers/nonContainerAutoGraderForm.tsx @@ -46,7 +46,7 @@ const NonContainerAutoGraderForm = () => { const toggleRegex = (e: React.ChangeEvent) => { setFormData(prevState => ({...prevState,isRegex : e.target.checked})) } - + useEffect(() => { RequestService.get(`/api/course/${courseId}/assignment/${assignmentId}/assignment-problems/`) .then((res) => { @@ -74,8 +74,10 @@ const NonContainerAutoGraderForm = () => { RequestService.post(`/api/course/${courseId}/assignment/${assignmentId}/non-container-auto-graders/`, finalFormData) .then(() => { setAlert({ autoDelete: true, type: 'success', message: 'Non-Container Auto-Grader Added' }) - history.goBack() + // history.goBack() + history.push(`/course/${courseId}/assignment/${assignmentId}?refreshProblems=true`); }) + .catch((err: ExpressValidationError[] | Error) => { const message = Array.isArray(err) ? err.map((e) => `${e.param} ${e.msg}`).join(', ') : err.message const newFields = applyStylesToErrorFields(err, formData, textStyles.errorField) @@ -110,13 +112,13 @@ const NonContainerAutoGraderForm = () => { - +

- +
diff --git a/devU-client/src/components/pages/gradebook/gradebookInstructorPage.tsx b/devU-client/src/components/pages/gradebook/gradebookInstructorPage.tsx index e1440dd..44575ab 100644 --- a/devU-client/src/components/pages/gradebook/gradebookInstructorPage.tsx +++ b/devU-client/src/components/pages/gradebook/gradebookInstructorPage.tsx @@ -1,16 +1,15 @@ -import React, { useEffect, useState } from 'react' +import React, {useEffect, useState} from 'react' -import { Assignment, AssignmentScore, User, UserCourse } from 'devu-shared-modules' +import {Assignment, AssignmentScore, User, UserCourse} from 'devu-shared-modules' import PageWrapper from 'components/shared/layouts/pageWrapper' import LoadingOverlay from 'components/shared/loaders/loadingOverlay' -import TextField from 'components/shared/inputs/textField' import ErrorPage from '../errorPage/errorPage' import RequestService from 'services/request.service' import styles from './gradebookPage.scss' -import { useParams } from 'react-router-dom' +import {useParams} from 'react-router-dom' type TableProps = { users: User[] @@ -19,29 +18,21 @@ type TableProps = { assignmentScores: AssignmentScore[] } type RowProps = { - index: number + index: Number user: User userCourse: UserCourse assignments: Assignment[] assignmentScores: AssignmentScore[] } -const TableRow = ({ index, user, userCourse, assignments, assignmentScores }: RowProps) => { - // style table row to alternating colors based on index odd?even - const rowClass = index % 2 === 0 ? 'evenRow' : 'oddRow'; - - // dont show row if dropped - if (userCourse.dropped) { - return (<>) - } - +const TableRow = ({index, user, userCourse, assignments, assignmentScores}: RowProps) => { return ( - + {index} {user.email} - {/* {user.externalId} */} + {user.externalId} {user.preferredName} - {/* {userCourse.dropped.toString()} */} + {userCourse.dropped.toString()} {assignments.map(a => ( {assignmentScores.find(as => as.assignmentId === a.id)?.score ?? 'N/A'} ))} @@ -49,16 +40,16 @@ const TableRow = ({ index, user, userCourse, assignments, assignmentScores }: Ro ) } -const GradebookTable = ({ users, userCourses, assignments, assignmentScores }: TableProps) => { +const GradebookTable = ({users, userCourses, assignments, assignmentScores}: TableProps) => { return ( - - +
#
+ - {/* */} + - {/* */} + {assignments.map((a) => { - return () + return ( ) })} {users.map((u, index) => ( { const [userCourses, setUserCourses] = useState(new Array()) //All user-course connections for the course const [assignments, setAssignments] = useState(new Array()) //All assignments in the course const [assignmentScores, setAssignmentScores] = useState(new Array()) //All assignment scores for assignments in the course - - const { courseId } = useParams<{ courseId: string }>() - + + const { courseId } = useParams<{courseId: string}>() + useEffect(() => { fetchData() - }, []) - + }, []) + const fetchData = async () => { try { const userCourses = await RequestService.get(`/api/course/${courseId}/user-courses/`) @@ -95,12 +86,12 @@ const GradebookInstructorPage = () => { const users = await RequestService.get(`/api/users/course/${courseId}`) setUsers(users) - - const assignments = await RequestService.get(`/api/course/${courseId}/assignments`) + + const assignments = await RequestService.get( `/api/course/${courseId}/assignments` ) assignments.sort((a, b) => (Date.parse(a.startDate) - Date.parse(b.startDate))) //Sort by assignment's start date setAssignments(assignments) - const assignmentScores = await RequestService.get(`/api/course/${courseId}/assignment-scores`) + const assignmentScores = await RequestService.get( `/api/course/${courseId}/assignment-scores` ) setAssignmentScores(assignmentScores) } catch (error: any) { @@ -113,28 +104,13 @@ const GradebookInstructorPage = () => { if (loading) return if (error) return - const handleStudentSearch = () => { - - } - return ( - {/*
*/} -

Instructor Gradebook

- {/*
*/} -
-
-

Key: ! = late, - = no submission

-
- -
-
- +

Instructor Gradebook

+
+
+ { if (loading) return if (error) return const history = useHistory(); + const handleCourseClick = (courseId: any) => { + history.push(`/course/${courseId}`); // Assuming your course page route is '/course/:courseId' + } return(
@@ -89,15 +92,19 @@ const HomePage = () => {
{instructorCourses.map((course) => ( -
- handleCourseClick(course.id)} style={{ cursor: 'pointer' }}> +
))}
{enrollCourses && enrollCourses.map((course) => ( -
+
handleCourseClick(course.id)} style={{ cursor: 'pointer' }}> +
))} @@ -114,7 +121,9 @@ const HomePage = () => {
{pastCourses && pastCourses.map((course) => ( -
+
handleCourseClick(course.id)} style={{ cursor: 'pointer' }}> {
{upcomingCourses && upcomingCourses.map((course) => ( -
+
handleCourseClick(course.id)} style={{ cursor: 'pointer' }}>
))} diff --git a/devU-client/src/components/pages/submissions/submissionDetailPage.tsx b/devU-client/src/components/pages/submissions/submissionDetailPage.tsx index 1bd9c87..3e80558 100644 --- a/devU-client/src/components/pages/submissions/submissionDetailPage.tsx +++ b/devU-client/src/components/pages/submissions/submissionDetailPage.tsx @@ -15,10 +15,13 @@ import 'react-datepicker/dist/react-datepicker.css' //import CardContent from '@mui/material/CardContent' import {CardActionArea} from '@mui/material' import {prettyPrintDateTime} from "../../../utils/date.utils"; +//import React, { useState } from 'react'; +//import { Document, Page } from 'react-pdf'; +//import StickyNote from 'react-sticky-notes'; const SubmissionDetailPage = () => { - + // const [notes, setNotes] = useState([]); const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [setAlert] = useActionless(SET_ALERT) @@ -107,6 +110,7 @@ const SubmissionDetailPage = () => { } } + @@ -114,6 +118,16 @@ const SubmissionDetailPage = () => { if (error) return //const history = useHistory() //var submission_form = JSON.parse(submission?.content); + + /* const handleAddNote = () => { + setNotes([ + + { + id: notes.length + 1, + content: '', + position: { x: 100, y: 100 }, // Initial position + }, + ]);*/ return(
# EmailExternal IDExternal ID Preferred NameDroppedDropped{a.name}{a.name}