From 72f3025fbaa0b0fa7228db0025089fc92f6a1f97 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:23:34 -0400 Subject: [PATCH 01/19] create submission table entry component --- .../SubmissionTable/SubmissionEntry.tsx | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx new file mode 100644 index 00000000..296f3759 --- /dev/null +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -0,0 +1,116 @@ +import { createColumnHelper } from "@tanstack/react-table"; +import { Button } from "react-bootstrap"; +import { Link } from "react-router-dom"; + +interface ISubmissionEntry { + id: number; + teamName: string; + assignment: string; + members: { name: string; id: number }[]; + links: { url: string; displayName: string }[]; + fileInfo: { name: string; size: string; dateModified: string }[]; +} + +const columnHelper = createColumnHelper(); + +const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void }) => { + const columns = [ + // Team Name column: Sorting enabled, search disabled + columnHelper.accessor('teamName', { + header: ({ column }) => ( +
+ Team Name + { + !column.getIsSorted() && + } +
+ ), + cell: (info) => ( + <> +
{info.getValue()}
+ + + ), + size: 25, + enableSorting: true, + enableColumnFilter: false, + enableGlobalFilter: false, + }), + + // Team Members column: No search, no sorting + columnHelper.accessor('members', { + header: () => 'Team Members', + cell: (info) => + info.getValue().map((member) => ( +
+ + {member.name} (Student {member.id}) + +
+ )), + size: 35, + enableSorting: false, + enableColumnFilter: false, + enableGlobalFilter: false, + }), + + // Links column: No search, no sorting + columnHelper.accessor('links', { + header: () => 'Links', + cell: (info) => ( +
+ {info.getValue().map((link, idx) => ( +
+ {link.displayName} +
+ ))} +
+ ), + size: 15, + enableSorting: false, + enableColumnFilter: false, + enableGlobalFilter: false, + }), + + // File Info column: No search, no sorting + columnHelper.accessor('fileInfo', { + header: () => 'File Info', + cell: (info) => ( +
+ {info.getValue().map((file, idx) => ( +
+
{file.name}
+
Size: {file.size}
+
Date Modified: {file.dateModified}
+
+ ))} +
+ ), + size: 25, + enableSorting: false, + enableColumnFilter: false, + enableGlobalFilter: false, + }), + + // History column: Links to history pages (No search or sorting) + columnHelper.display({ + id: 'history', + header: () => 'History', + cell: (info) => ( + History + ), + enableSorting: false, + enableColumnFilter: false, + enableGlobalFilter: false, + }), + ]; + + return columns; +}; + +export default SubmissionEntry; From 66a7f70f393017bc35cfd43d5622815a7277ee99 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:23:53 -0400 Subject: [PATCH 02/19] create submission list component --- .../SubmissionTable/SubmissionList.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/pages/Submissions/SubmissionTable/SubmissionList.tsx diff --git a/src/pages/Submissions/SubmissionTable/SubmissionList.tsx b/src/pages/Submissions/SubmissionTable/SubmissionList.tsx new file mode 100644 index 00000000..f0a65f0f --- /dev/null +++ b/src/pages/Submissions/SubmissionTable/SubmissionList.tsx @@ -0,0 +1,22 @@ +import Table from "components/Table/Table"; +import { useMemo } from "react"; +import SubmissionEntry from "./SubmissionEntry"; + +const SubmissionList = ({ submissions, onGradeClick }: { submissions: any[], onGradeClick: (id: number) => void }) => { + + const columns = useMemo(() => SubmissionEntry({ onGradeClick }), [onGradeClick]); + + return ( +
+ + + ); +}; + +export default SubmissionList; From ea660c5ff98062a3a717901e841435710e5fd27a Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:24:31 -0400 Subject: [PATCH 03/19] create submission view component --- src/pages/Submissions/SubmissionsView.tsx | 97 +++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/pages/Submissions/SubmissionsView.tsx diff --git a/src/pages/Submissions/SubmissionsView.tsx b/src/pages/Submissions/SubmissionsView.tsx new file mode 100644 index 00000000..39f8cf2d --- /dev/null +++ b/src/pages/Submissions/SubmissionsView.tsx @@ -0,0 +1,97 @@ +import { useEffect, useState } from "react"; +import { Col, Container, Form, Row } from "react-bootstrap"; +import SubmissionList from "./SubmissionTable/SubmissionList"; + +const SubmissionView = () => { + const [submissions, setSubmissions] = useState([]); + const [filteredSubmissions, setFilteredSubmissions] = useState([]); + const [assignmentFilter, setAssignmentFilter] = useState(""); + + // Dummy assignments for filtering + const assignments = ["Assignment 1", "Assignment 2", "Assignment 3"]; + + useEffect(() => { + // Simulating data fetching + const fetchSubmissions = async () => { + const data = [ + { + id: 1, + teamName: "Anonymized_Team_38121", + assignment: "Assignment 1", + members: [ + { name: "Student 10566", id: 10566 }, + { name: "Student 10559", id: 10559 }, + { name: "Student 10359", id: 10359 }, + ], + links: [ + { url: "https://github.com/example/repo", displayName: "GitHub Repository" }, + { url: "http://google.com", displayName: "Submission Link" }, + ], + fileInfo: [ + { name: "README.md", size: "14.9 KB", dateModified: "2024-10-03 23:36:57" }, + ], + }, + { + id: 2, + teamName: "Anonymized_Team_38122", + assignment: "Assignment 2", + members: [ + { name: "Student 10593", id: 10593 }, + { name: "Student 10623", id: 10623 }, + ], + links: [ + { url: "https://github.com/example/repo2", displayName: "GitHub Repository" }, + ], + fileInfo: [ + { name: "README.md", size: "11.7 KB", dateModified: "2024-10-01 12:15:00" }, + ], + }, + ]; + + setSubmissions(data); + setFilteredSubmissions(data); + }; + + fetchSubmissions(); + }, []); + + const handleGradeClick = (id: number) => { + console.log(`Assign Grade clicked for submission ID ${id}`); + }; + + const handleAssignmentChange = (e: React.ChangeEvent) => { + const selectedAssignment = e.target.value; + setAssignmentFilter(selectedAssignment); + if (selectedAssignment) { + setFilteredSubmissions(submissions.filter(sub => sub.assignment === selectedAssignment)); + } else { + setFilteredSubmissions(submissions); + } + }; + + return ( + + + +

Submissions

+
+ + Filter by Assignment + handleAssignmentChange(e as any)}> + + {assignments.map((assignment, index) => ( + + ))} + + + + + + + + + + ); +}; + +export default SubmissionView; From bd2161683b530a00c943e7fbf12fcf7168e4226e Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:30:02 -0400 Subject: [PATCH 04/19] add route for submission view --- src/App.tsx | 64 ++++++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 27736ba3..9230c739 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,45 +1,45 @@ -import React from "react"; +import RootLayout from "layout/Root"; +import { loadAssignment } from "pages/Assignments/AssignmentUtil"; +import AssignReviewer from "pages/Assignments/AssignReviewer"; +import CreateTeams from "pages/Assignments/CreateTeams"; +import ViewDelayedJobs from "pages/Assignments/ViewDelayedJobs"; +import ViewReports from "pages/Assignments/ViewReports"; +import ViewScores from "pages/Assignments/ViewScores"; +import ViewSubmissions from "pages/Assignments/ViewSubmissions"; +import Courses from "pages/Courses/Course"; +import CourseEditor from "pages/Courses/CourseEditor"; +import { loadCourseInstructorDataAndInstitutions } from "pages/Courses/CourseUtil"; +import Questionnaire from "pages/EditQuestionnaire/Questionnaire"; +import Home from "pages/Home"; +import Participants from "pages/Participants/Participant"; +import ParticipantEditor from "pages/Participants/ParticipantEditor"; +import { loadParticipantDataRolesAndInstitutions } from "pages/Participants/participantUtil"; +import EditProfile from "pages/Profile/Edit"; +import Reviews from "pages/Reviews/reviews"; +import SubmissionsView from "pages/Submissions/SubmissionsView"; +import TA from "pages/TA/TA"; +import TAEditor from "pages/TA/TAEditor"; +import { loadTAs } from "pages/TA/TAUtil"; import { createBrowserRouter, Navigate, RouterProvider } from "react-router-dom"; import AdministratorLayout from "./layout/Administrator"; import ManageUserTypes, { loader as loadUsers } from "./pages/Administrator/ManageUserTypes"; +import Assignment from "./pages/Assignments/Assignment"; +import AssignmentEditor from "./pages/Assignments/AssignmentEditor"; import Login from "./pages/Authentication/Login"; import Logout from "./pages/Authentication/Logout"; +import Email_the_author from "./pages/Email_the_author/email_the_author"; import InstitutionEditor, { loadInstitution } from "./pages/Institutions/InstitutionEditor"; import Institutions, { loadInstitutions } from "./pages/Institutions/Institutions"; import RoleEditor, { loadAvailableRole } from "./pages/Roles/RoleEditor"; import Roles, { loadRoles } from "./pages/Roles/Roles"; -import Assignment from "./pages/Assignments/Assignment"; -import AssignmentEditor from "./pages/Assignments/AssignmentEditor"; -import { loadAssignment } from "pages/Assignments/AssignmentUtil"; -import ErrorPage from "./router/ErrorPage"; -import ProtectedRoute from "./router/ProtectedRoute"; -import { ROLE } from "./utils/interfaces"; -import NotFound from "./router/NotFound"; -import Participants from "pages/Participants/Participant"; -import ParticipantEditor from "pages/Participants/ParticipantEditor"; -import { loadParticipantDataRolesAndInstitutions } from "pages/Participants/participantUtil"; -import RootLayout from "layout/Root"; -import UserEditor from "./pages/Users/UserEditor"; import Users from "./pages/Users/User"; +import UserEditor from "./pages/Users/UserEditor"; import { loadUserDataRolesAndInstitutions } from "./pages/Users/userUtil"; -import Home from "pages/Home"; -import Questionnaire from "pages/EditQuestionnaire/Questionnaire"; -import Courses from "pages/Courses/Course"; -import CourseEditor from "pages/Courses/CourseEditor"; -import { loadCourseInstructorDataAndInstitutions } from "pages/Courses/CourseUtil"; -import TA from "pages/TA/TA"; -import TAEditor from "pages/TA/TAEditor"; -import { loadTAs } from "pages/TA/TAUtil"; import ReviewTable from "./pages/ViewTeamGrades/ReviewTable"; -import EditProfile from "pages/Profile/Edit"; -import Reviews from "pages/Reviews/reviews"; -import Email_the_author from "./pages/Email_the_author/email_the_author"; -import CreateTeams from "pages/Assignments/CreateTeams"; -import AssignReviewer from "pages/Assignments/AssignReviewer"; -import ViewSubmissions from "pages/Assignments/ViewSubmissions"; -import ViewScores from "pages/Assignments/ViewScores"; -import ViewReports from "pages/Assignments/ViewReports"; -import ViewDelayedJobs from "pages/Assignments/ViewDelayedJobs"; +import ErrorPage from "./router/ErrorPage"; +import NotFound from "./router/NotFound"; +import ProtectedRoute from "./router/ProtectedRoute"; +import { ROLE } from "./utils/interfaces"; function App() { const router = createBrowserRouter([ { @@ -122,6 +122,10 @@ function App() { }, ], }, + { + path: "student_tasks", + element: } leastPrivilegeRole={ROLE.TA} />, + }, { path: "student_tasks/participants", element: , From 0d6e04783c399208b1d88ef702e8f42bc13a99a1 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:30:59 -0400 Subject: [PATCH 05/19] fix styling for container --- src/pages/ViewTeamGrades/grades.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/ViewTeamGrades/grades.scss b/src/pages/ViewTeamGrades/grades.scss index 8434ae5c..7b9252ec 100644 --- a/src/pages/ViewTeamGrades/grades.scss +++ b/src/pages/ViewTeamGrades/grades.scss @@ -227,7 +227,7 @@ .container { display: flex; justify-content: space-between; /* Adjust as needed */ - width: 80%; /* Ensure the container takes up the full width */ + width: 100%; /* Ensure the container takes up the full width */ } From b5b07f89c509d721a75d457a4faca4e5281b0e63 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:45:22 -0400 Subject: [PATCH 06/19] test submission view component --- .../Submissions/SubmissionsView.test.tsx | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/pages/Submissions/SubmissionsView.test.tsx diff --git a/src/pages/Submissions/SubmissionsView.test.tsx b/src/pages/Submissions/SubmissionsView.test.tsx new file mode 100644 index 00000000..d0df159b --- /dev/null +++ b/src/pages/Submissions/SubmissionsView.test.tsx @@ -0,0 +1,54 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import SubmissionView from './SubmissionsView'; + +describe('SubmissionsView', () => { + it('renders the title and filter', () => { + render( + + + + ); + + const title = screen.getByText('Submissions'); + const filter = screen.getByLabelText('Filter by Assignment'); + + expect(title).toBeTruthy(); + expect(filter).toBeTruthy(); + }); + + it('filters submissions based on selected assignment', async () => { + render( + + + + ); + + // Select an assignment to filter + const select = screen.getByLabelText('Filter by Assignment'); + fireEvent.change(select, { target: { value: 'Assignment 1' } }); + + // Check if the filtered submission is displayed + expect(await screen.findByText('Anonymized_Team_38121')).toBeTruthy(); + expect(screen.queryByText('Anonymized_Team_38122')).toBeFalsy(); + }); + + it('shows all submissions when no filter is applied', async () => { + render( + + + + ); + + // Select an assignment to filter + const select = screen.getByLabelText('Filter by Assignment'); + fireEvent.change(select, { target: { value: 'Assignment 1' } }); + + // Reset filter + fireEvent.change(select, { target: { value: '' } }); + + // Check if all submissions are displayed + expect(await screen.findByText('Anonymized_Team_38121')).toBeTruthy(); + expect(await screen.findByText('Anonymized_Team_38122')).toBeTruthy(); + }); +}); From e235db85da71e4e88761787b73e5fd33cd0d8eb3 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:45:34 -0400 Subject: [PATCH 07/19] test submission list component --- .../SubmissionTable/SubmissionList.test.tsx | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 src/pages/Submissions/SubmissionTable/SubmissionList.test.tsx diff --git a/src/pages/Submissions/SubmissionTable/SubmissionList.test.tsx b/src/pages/Submissions/SubmissionTable/SubmissionList.test.tsx new file mode 100644 index 00000000..5f6ab171 --- /dev/null +++ b/src/pages/Submissions/SubmissionTable/SubmissionList.test.tsx @@ -0,0 +1,68 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import SubmissionList from './SubmissionList'; + +const mockSubmissions = [ + { + id: 1, + teamName: 'Team B', + assignment: 'Assignment 1', + members: [{ name: 'Student 1', id: 1 }], + links: [], + fileInfo: [], + }, + { + id: 2, + teamName: 'Team A', + assignment: 'Assignment 1', + members: [{ name: 'Student 2', id: 2 }], + links: [], + fileInfo: [], + }, +]; + +const mockOnGradeClick = jest.fn(); + +describe('SubmissionList', () => { + it('renders submission entries correctly', () => { + render( + + + + ); + + // Check if submission entry is rendered + expect(screen.getByText('Team B')).toBeTruthy(); + expect(screen.getByText('Team A')).toBeTruthy(); + }); + + it('sorts the submissions by team name', () => { + render( + + + + ); + + // Click the team name header to sort ascending + const teamNameHeader = screen.getByText('Team Name'); + fireEvent.click(teamNameHeader); + + // Get the rows that contain submission entries + const rows = screen.getAllByRole('row'); + + // Check the order of the first two submission rows (excluding the header) + expect(rows[1].innerHTML).toContain('Team A'); + expect(rows[2].innerHTML).toContain('Team B'); + + // Click again to sort descending + fireEvent.click(teamNameHeader); + + // Get the rows again after sorting + const sortedRows = screen.getAllByRole('row'); + + // Check the order of the first two submission rows (excluding the header) + expect(sortedRows[1].innerHTML).toContain('Team B'); + expect(sortedRows[2].innerHTML).toContain('Team A'); + }); + +}); From e8939494eb74d620da5c5d0c3d0f894cc95c8634 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 17:45:48 -0400 Subject: [PATCH 08/19] test submission entry component --- .../SubmissionTable/SubmissionEntry.test.tsx | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/pages/Submissions/SubmissionTable/SubmissionEntry.test.tsx diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.test.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.test.tsx new file mode 100644 index 00000000..41e2790b --- /dev/null +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.test.tsx @@ -0,0 +1,43 @@ +import { fireEvent, render, screen } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import SubmissionList from './SubmissionList'; + +const mockSubmissions = [ + { + id: 1, + teamName: 'Anonymized_Team_38121', + assignment: 'Assignment 1', + members: [{ name: 'Student 1', id: 1 }], + links: [], + fileInfo: [], + }, +]; + +const mockOnGradeClick = jest.fn(); + +describe('SubmissionEntry', () => { + it('displays the correct team name', () => { + render( + + + + ); + + // Check if team name is rendered correctly + expect(screen.getByText('Anonymized_Team_38121')).toBeTruthy(); + }); + + it('calls onGradeClick when the grade button is clicked', () => { + render( + + + + ); + + // Simulate the button click + const button = screen.getByRole('button', { name: /Assign Grade/i }); + fireEvent.click(button); + + expect(mockOnGradeClick).toHaveBeenCalledWith(mockSubmissions[0].id); + }); +}); From be613f70e689ae9db8f08013f150a19d7ced1417 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sun, 13 Oct 2024 18:00:18 -0400 Subject: [PATCH 09/19] move existing individual submission view to submissions directory --- src/App.tsx | 4 ++-- .../SubmissionView.tsx} | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/pages/{Assignments/ViewSubmissions.tsx => Submissions/SubmissionView.tsx} (94%) diff --git a/src/App.tsx b/src/App.tsx index 9230c739..7e07e27a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,6 @@ import CreateTeams from "pages/Assignments/CreateTeams"; import ViewDelayedJobs from "pages/Assignments/ViewDelayedJobs"; import ViewReports from "pages/Assignments/ViewReports"; import ViewScores from "pages/Assignments/ViewScores"; -import ViewSubmissions from "pages/Assignments/ViewSubmissions"; import Courses from "pages/Courses/Course"; import CourseEditor from "pages/Courses/CourseEditor"; import { loadCourseInstructorDataAndInstitutions } from "pages/Courses/CourseUtil"; @@ -17,6 +16,7 @@ import { loadParticipantDataRolesAndInstitutions } from "pages/Participants/part import EditProfile from "pages/Profile/Edit"; import Reviews from "pages/Reviews/reviews"; import SubmissionsView from "pages/Submissions/SubmissionsView"; +import SubmissionView from "pages/Submissions/SubmissionView"; import TA from "pages/TA/TA"; import TAEditor from "pages/TA/TAEditor"; import { loadTAs } from "pages/TA/TAUtil"; @@ -72,7 +72,7 @@ function App() { }, { path: "assignments/edit/:id/viewsubmissions", - element: , + element: , loader: loadAssignment, }, { diff --git a/src/pages/Assignments/ViewSubmissions.tsx b/src/pages/Submissions/SubmissionView.tsx similarity index 94% rename from src/pages/Assignments/ViewSubmissions.tsx rename to src/pages/Submissions/SubmissionView.tsx index d9fd69b1..48b31f77 100644 --- a/src/pages/Assignments/ViewSubmissions.tsx +++ b/src/pages/Submissions/SubmissionView.tsx @@ -1,8 +1,8 @@ import React, { useMemo } from 'react'; -import { Button, Container, Row, Col } from 'react-bootstrap'; +import { Button, Col, Container, Row } from 'react-bootstrap'; // import { useNavigate } from 'react-router-dom'; -import Table from "components/Table/Table"; import { createColumnHelper } from "@tanstack/react-table"; +import Table from "components/Table/Table"; import { useLoaderData } from 'react-router-dom'; interface ISubmission { @@ -12,7 +12,7 @@ interface ISubmission { const columnHelper = createColumnHelper(); -const ViewSubmissions: React.FC = () => { +const SubmissionView: React.FC = () => { const assignment: any = useLoaderData(); // const navigate = useNavigate(); @@ -81,4 +81,4 @@ const ViewSubmissions: React.FC = () => { ); }; -export default ViewSubmissions; \ No newline at end of file +export default SubmissionView; \ No newline at end of file From 771c84b619477fe84f6340820060f56302c33fe4 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Sat, 19 Oct 2024 13:33:48 -0400 Subject: [PATCH 10/19] update view to include 23 mocked users --- src/pages/Submissions/SubmissionView.tsx | 7 -- src/pages/Submissions/SubmissionsView.tsx | 83 +++++++++++++---------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/src/pages/Submissions/SubmissionView.tsx b/src/pages/Submissions/SubmissionView.tsx index 48b31f77..4e8f14cc 100644 --- a/src/pages/Submissions/SubmissionView.tsx +++ b/src/pages/Submissions/SubmissionView.tsx @@ -70,13 +70,6 @@ const SubmissionView: React.FC = () => { /> - {/* - - - - */} ); }; diff --git a/src/pages/Submissions/SubmissionsView.tsx b/src/pages/Submissions/SubmissionsView.tsx index 39f8cf2d..1ed53ba5 100644 --- a/src/pages/Submissions/SubmissionsView.tsx +++ b/src/pages/Submissions/SubmissionsView.tsx @@ -13,40 +13,41 @@ const SubmissionView = () => { useEffect(() => { // Simulating data fetching const fetchSubmissions = async () => { - const data = [ - { - id: 1, - teamName: "Anonymized_Team_38121", - assignment: "Assignment 1", - members: [ - { name: "Student 10566", id: 10566 }, - { name: "Student 10559", id: 10559 }, - { name: "Student 10359", id: 10359 }, - ], - links: [ - { url: "https://github.com/example/repo", displayName: "GitHub Repository" }, - { url: "http://google.com", displayName: "Submission Link" }, - ], - fileInfo: [ - { name: "README.md", size: "14.9 KB", dateModified: "2024-10-03 23:36:57" }, - ], - }, - { - id: 2, - teamName: "Anonymized_Team_38122", - assignment: "Assignment 2", - members: [ - { name: "Student 10593", id: 10593 }, - { name: "Student 10623", id: 10623 }, - ], - links: [ - { url: "https://github.com/example/repo2", displayName: "GitHub Repository" }, - ], - fileInfo: [ - { name: "README.md", size: "11.7 KB", dateModified: "2024-10-01 12:15:00" }, - ], - }, - ]; + const date = new Date(Date.parse('04 Dec 2021 00:12:00 GMT')); + const data = Array.from({ length: 23 }, (_, i) => { + const id = i + 1; + const teamNumber = 38121 + i; + const assignmentNumber = (i % 5) + 1; + const studentCount = (i % 3) + 1; + const currentDate = new Date(new Date().setDate(date.getDate() + i)); + + const members = Array.from({ length: studentCount }, (_, j) => ({ + name: `Student ${10000 + i * 10 + j}`, + id: 10000 + i * 10 + j, + })); + + const links = [ + { url: `https://github.com/example/repo${id}`, displayName: "GitHub Repository" }, + { url: `http://example.com/submission${id}`, displayName: "Submission Link" }, + ]; + + const fileInfo = [ + { + name: `README.md`, + size: `${(Math.random() * 15 + 10).toFixed(1)} KB`, + dateModified: formatDate(currentDate), + }, + ]; + + return { + id, + teamName: `Anonymized_Team_${teamNumber}`, + assignment: `Assignment ${assignmentNumber}`, + members, + links, + fileInfo, + }; + }); setSubmissions(data); setFilteredSubmissions(data); @@ -55,6 +56,20 @@ const SubmissionView = () => { fetchSubmissions(); }, []); + const formatDate = (date: Date) => { + const padZero = (num: number) => String(num).padStart(2, '0'); + + const year = String(date.getFullYear()) // Last two digits of the year + const month = padZero(date.getMonth() + 1); // Months are zero-based, so we add 1 + const day = padZero(date.getDate()); + + const hours = padZero(date.getHours()); + const minutes = padZero(date.getMinutes()); + const seconds = padZero(date.getSeconds()); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + } + const handleGradeClick = (id: number) => { console.log(`Assign Grade clicked for submission ID ${id}`); }; From 1eb5d7b8d677ab083a999119abc1434b812e4a5a Mon Sep 17 00:00:00 2001 From: mjfeng Date: Sun, 27 Oct 2024 23:52:44 -0400 Subject: [PATCH 11/19] submission history view --- package-lock.json | 45 +++++- src/App.tsx | 5 + .../SubmissionHistoryView.test.tsx | 1 + .../Submissions/SubmissionHistoryView.tsx | 136 ++++++++++++++++++ .../SubmissionTable/SubmissionEntry.tsx | 2 +- 5 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 src/pages/Submissions/SubmissionHistoryView.test.tsx create mode 100644 src/pages/Submissions/SubmissionHistoryView.tsx diff --git a/package-lock.json b/package-lock.json index 3ef4561e..19bc6dc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.4.0", "bootstrap": "^5.3.3", - "chart.js": "^3.7.0", + "chart.js": "^4.1.1", "formik": "^2.2.9", "jquery": "^3.7.1", "jwt-decode": "^3.1.2", @@ -38,7 +38,7 @@ "react-redux": "^8.0.5", "react-router-dom": "^6.11.1", "react-scripts": "^5.0.1", - "recharts": "^2.12.3", + "recharts": "^2.0.0", "redux-persist": "^6.0.0", "sass": "^1.62.1", "save": "^2.9.0", @@ -3045,6 +3045,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", + "license": "MIT" + }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -5760,9 +5766,16 @@ } }, "node_modules/chart.js": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", - "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz", + "integrity": "sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } }, "node_modules/check-types": { "version": "11.2.3", @@ -16603,6 +16616,28 @@ "node": ">= 0.8" } }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", diff --git a/src/App.tsx b/src/App.tsx index 7e07e27a..83b6f354 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,7 @@ import EditProfile from "pages/Profile/Edit"; import Reviews from "pages/Reviews/reviews"; import SubmissionsView from "pages/Submissions/SubmissionsView"; import SubmissionView from "pages/Submissions/SubmissionView"; +import SubmissionHistoryView from "./pages/Submissions/SubmissionHistoryView"; import TA from "pages/TA/TA"; import TAEditor from "pages/TA/TAEditor"; import { loadTAs } from "pages/TA/TAUtil"; @@ -75,6 +76,10 @@ function App() { element: , loader: loadAssignment, }, + { + path: "submissions/history/:submissionId", + element: } leastPrivilegeRole={ROLE.TA} />, + }, { path: "assignments/edit/:id/viewscores", element: , diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx b/src/pages/Submissions/SubmissionHistoryView.test.tsx new file mode 100644 index 00000000..5040fd93 --- /dev/null +++ b/src/pages/Submissions/SubmissionHistoryView.test.tsx @@ -0,0 +1 @@ +// WIP \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionHistoryView.tsx b/src/pages/Submissions/SubmissionHistoryView.tsx new file mode 100644 index 00000000..4f16bdf6 --- /dev/null +++ b/src/pages/Submissions/SubmissionHistoryView.tsx @@ -0,0 +1,136 @@ +import { useEffect, useState } from 'react'; +import { Container } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; +import { + createColumnHelper, + flexRender, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table'; + +interface HistoryEntry { + teamId: number; + operation: string; + user: string; + content: string; + created: string; +} + +const SubmissionHistoryView = () => { + const [history, setHistory] = useState([]); + + // Does nothing at the moment but a real implementation would likely + // retrieve submission history data via the submission ID + const { submissionId } = useParams(); + + const columnHelper = createColumnHelper(); + + const columns = [ + columnHelper.accessor('teamId', { + header: 'Team Id', + cell: info => info.getValue(), + }), + columnHelper.accessor('operation', { + header: 'Operation', + cell: info => info.getValue(), + }), + columnHelper.accessor('user', { + header: 'User', + cell: info => info.getValue(), + }), + columnHelper.accessor('content', { + header: 'Content', + cell: info => info.getValue(), + }), + columnHelper.accessor('created', { + header: 'Created', + cell: info => info.getValue(), + }), + ]; + + // Load data, dummy data for now + useEffect(() => { + const dummyData: HistoryEntry[] = [ + { + teamId: 38121, + operation: 'Submit Hyperlink', + user: 'adgorkar', + content: 'https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2', + created: '2024-09-17 22:38:09 -0400' + }, + { + teamId: 38121, + operation: 'Submit Hyperlink', + user: 'adgorkar', + content: 'http://152.7.176.240:8080/', + created: '2024-09-27 18:32:10 -0400' + }, + { + teamId: 38121, + operation: 'Submit File', + user: 'adgorkar', + content: 'README.md', + created: '2024-09-29 17:52:24 -0400' + }, + { + teamId: 38121, + operation: 'Remove File', + user: 'adgorkar', + content: 'README.md', + created: '2024-10-03 23:36:03 -0400' + }, + { + teamId: 38121, + operation: 'Submit File', + user: 'adgorkar', + content: 'README_4_.md', + created: '2024-10-03 23:36:57 -0400' + } + ]; + setHistory(dummyData); + }, [submissionId]); + + const table = useReactTable({ + data: history, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + +
+
+ + + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + ))} + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + +
+

Submission Record

+
+ {flexRender(header.column.columnDef.header, header.getContext())} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ + ); +}; + +export default SubmissionHistoryView; \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx index 296f3759..90842088 100644 --- a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -102,7 +102,7 @@ const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void id: 'history', header: () => 'History', cell: (info) => ( - History + History ), enableSorting: false, enableColumnFilter: false, From eb6ec5ebf6a05c98c632031d0a1053cf9e2a8d0a Mon Sep 17 00:00:00 2001 From: mjfeng Date: Sun, 27 Oct 2024 23:58:31 -0400 Subject: [PATCH 12/19] Revert "submission history view" This reverts commit 1eb5d7b8d677ab083a999119abc1434b812e4a5a. --- package-lock.json | 45 +----- src/App.tsx | 5 - .../SubmissionHistoryView.test.tsx | 1 - .../Submissions/SubmissionHistoryView.tsx | 136 ------------------ .../SubmissionTable/SubmissionEntry.tsx | 2 +- 5 files changed, 6 insertions(+), 183 deletions(-) delete mode 100644 src/pages/Submissions/SubmissionHistoryView.test.tsx delete mode 100644 src/pages/Submissions/SubmissionHistoryView.tsx diff --git a/package-lock.json b/package-lock.json index 19bc6dc8..3ef4561e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,7 +24,7 @@ "@types/react-router-dom": "^5.3.3", "axios": "^1.4.0", "bootstrap": "^5.3.3", - "chart.js": "^4.1.1", + "chart.js": "^3.7.0", "formik": "^2.2.9", "jquery": "^3.7.1", "jwt-decode": "^3.1.2", @@ -38,7 +38,7 @@ "react-redux": "^8.0.5", "react-router-dom": "^6.11.1", "react-scripts": "^5.0.1", - "recharts": "^2.0.0", + "recharts": "^2.12.3", "redux-persist": "^6.0.0", "sass": "^1.62.1", "save": "^2.9.0", @@ -3045,12 +3045,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kurkle/color": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", - "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==", - "license": "MIT" - }, "node_modules/@leichtgewicht/ip-codec": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", @@ -5766,16 +5760,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.5.tgz", - "integrity": "sha512-CVVjg1RYTJV9OCC8WeJPMx8gsV8K6WIyIEQUE3ui4AR9Hfgls9URri6Ja3hyMVBbTF8Q2KFa19PE815gWcWhng==", - "license": "MIT", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", + "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" }, "node_modules/check-types": { "version": "11.2.3", @@ -16616,28 +16603,6 @@ "node": ">= 0.8" } }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", diff --git a/src/App.tsx b/src/App.tsx index 83b6f354..7e07e27a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,7 +17,6 @@ import EditProfile from "pages/Profile/Edit"; import Reviews from "pages/Reviews/reviews"; import SubmissionsView from "pages/Submissions/SubmissionsView"; import SubmissionView from "pages/Submissions/SubmissionView"; -import SubmissionHistoryView from "./pages/Submissions/SubmissionHistoryView"; import TA from "pages/TA/TA"; import TAEditor from "pages/TA/TAEditor"; import { loadTAs } from "pages/TA/TAUtil"; @@ -76,10 +75,6 @@ function App() { element: , loader: loadAssignment, }, - { - path: "submissions/history/:submissionId", - element: } leastPrivilegeRole={ROLE.TA} />, - }, { path: "assignments/edit/:id/viewscores", element: , diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx b/src/pages/Submissions/SubmissionHistoryView.test.tsx deleted file mode 100644 index 5040fd93..00000000 --- a/src/pages/Submissions/SubmissionHistoryView.test.tsx +++ /dev/null @@ -1 +0,0 @@ -// WIP \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionHistoryView.tsx b/src/pages/Submissions/SubmissionHistoryView.tsx deleted file mode 100644 index 4f16bdf6..00000000 --- a/src/pages/Submissions/SubmissionHistoryView.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Container } from 'react-bootstrap'; -import { useParams } from 'react-router-dom'; -import { - createColumnHelper, - flexRender, - getCoreRowModel, - useReactTable, -} from '@tanstack/react-table'; - -interface HistoryEntry { - teamId: number; - operation: string; - user: string; - content: string; - created: string; -} - -const SubmissionHistoryView = () => { - const [history, setHistory] = useState([]); - - // Does nothing at the moment but a real implementation would likely - // retrieve submission history data via the submission ID - const { submissionId } = useParams(); - - const columnHelper = createColumnHelper(); - - const columns = [ - columnHelper.accessor('teamId', { - header: 'Team Id', - cell: info => info.getValue(), - }), - columnHelper.accessor('operation', { - header: 'Operation', - cell: info => info.getValue(), - }), - columnHelper.accessor('user', { - header: 'User', - cell: info => info.getValue(), - }), - columnHelper.accessor('content', { - header: 'Content', - cell: info => info.getValue(), - }), - columnHelper.accessor('created', { - header: 'Created', - cell: info => info.getValue(), - }), - ]; - - // Load data, dummy data for now - useEffect(() => { - const dummyData: HistoryEntry[] = [ - { - teamId: 38121, - operation: 'Submit Hyperlink', - user: 'adgorkar', - content: 'https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2', - created: '2024-09-17 22:38:09 -0400' - }, - { - teamId: 38121, - operation: 'Submit Hyperlink', - user: 'adgorkar', - content: 'http://152.7.176.240:8080/', - created: '2024-09-27 18:32:10 -0400' - }, - { - teamId: 38121, - operation: 'Submit File', - user: 'adgorkar', - content: 'README.md', - created: '2024-09-29 17:52:24 -0400' - }, - { - teamId: 38121, - operation: 'Remove File', - user: 'adgorkar', - content: 'README.md', - created: '2024-10-03 23:36:03 -0400' - }, - { - teamId: 38121, - operation: 'Submit File', - user: 'adgorkar', - content: 'README_4_.md', - created: '2024-10-03 23:36:57 -0400' - } - ]; - setHistory(dummyData); - }, [submissionId]); - - const table = useReactTable({ - data: history, - columns, - getCoreRowModel: getCoreRowModel(), - }); - - return ( - -
- - - - - - {table.getHeaderGroups().map(headerGroup => ( - - {headerGroup.headers.map(header => ( - - ))} - - ))} - - - {table.getRowModel().rows.map(row => ( - - {row.getVisibleCells().map(cell => ( - - ))} - - ))} - -
-

Submission Record

-
- {flexRender(header.column.columnDef.header, header.getContext())} -
- {flexRender(cell.column.columnDef.cell, cell.getContext())} -
-
-
- ); -}; - -export default SubmissionHistoryView; \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx index 90842088..296f3759 100644 --- a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -102,7 +102,7 @@ const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void id: 'history', header: () => 'History', cell: (info) => ( - History + History ), enableSorting: false, enableColumnFilter: false, From 64975a8f6d1b83819692a639c3477cb17251318d Mon Sep 17 00:00:00 2001 From: mjfeng Date: Mon, 28 Oct 2024 00:05:04 -0400 Subject: [PATCH 13/19] woops made a mistake, changes without the updated package file --- src/App.tsx | 5 + .../SubmissionHistoryView.test.tsx.wip | 1 + .../Submissions/SubmissionHistoryView.tsx | 136 ++++++++++++++++++ .../SubmissionTable/SubmissionEntry.tsx | 2 +- 4 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/pages/Submissions/SubmissionHistoryView.test.tsx.wip create mode 100644 src/pages/Submissions/SubmissionHistoryView.tsx diff --git a/src/App.tsx b/src/App.tsx index 7e07e27a..83b6f354 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,6 +17,7 @@ import EditProfile from "pages/Profile/Edit"; import Reviews from "pages/Reviews/reviews"; import SubmissionsView from "pages/Submissions/SubmissionsView"; import SubmissionView from "pages/Submissions/SubmissionView"; +import SubmissionHistoryView from "./pages/Submissions/SubmissionHistoryView"; import TA from "pages/TA/TA"; import TAEditor from "pages/TA/TAEditor"; import { loadTAs } from "pages/TA/TAUtil"; @@ -75,6 +76,10 @@ function App() { element: , loader: loadAssignment, }, + { + path: "submissions/history/:submissionId", + element: } leastPrivilegeRole={ROLE.TA} />, + }, { path: "assignments/edit/:id/viewscores", element: , diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip b/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip new file mode 100644 index 00000000..5040fd93 --- /dev/null +++ b/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip @@ -0,0 +1 @@ +// WIP \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionHistoryView.tsx b/src/pages/Submissions/SubmissionHistoryView.tsx new file mode 100644 index 00000000..4f16bdf6 --- /dev/null +++ b/src/pages/Submissions/SubmissionHistoryView.tsx @@ -0,0 +1,136 @@ +import { useEffect, useState } from 'react'; +import { Container } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; +import { + createColumnHelper, + flexRender, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table'; + +interface HistoryEntry { + teamId: number; + operation: string; + user: string; + content: string; + created: string; +} + +const SubmissionHistoryView = () => { + const [history, setHistory] = useState([]); + + // Does nothing at the moment but a real implementation would likely + // retrieve submission history data via the submission ID + const { submissionId } = useParams(); + + const columnHelper = createColumnHelper(); + + const columns = [ + columnHelper.accessor('teamId', { + header: 'Team Id', + cell: info => info.getValue(), + }), + columnHelper.accessor('operation', { + header: 'Operation', + cell: info => info.getValue(), + }), + columnHelper.accessor('user', { + header: 'User', + cell: info => info.getValue(), + }), + columnHelper.accessor('content', { + header: 'Content', + cell: info => info.getValue(), + }), + columnHelper.accessor('created', { + header: 'Created', + cell: info => info.getValue(), + }), + ]; + + // Load data, dummy data for now + useEffect(() => { + const dummyData: HistoryEntry[] = [ + { + teamId: 38121, + operation: 'Submit Hyperlink', + user: 'adgorkar', + content: 'https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2', + created: '2024-09-17 22:38:09 -0400' + }, + { + teamId: 38121, + operation: 'Submit Hyperlink', + user: 'adgorkar', + content: 'http://152.7.176.240:8080/', + created: '2024-09-27 18:32:10 -0400' + }, + { + teamId: 38121, + operation: 'Submit File', + user: 'adgorkar', + content: 'README.md', + created: '2024-09-29 17:52:24 -0400' + }, + { + teamId: 38121, + operation: 'Remove File', + user: 'adgorkar', + content: 'README.md', + created: '2024-10-03 23:36:03 -0400' + }, + { + teamId: 38121, + operation: 'Submit File', + user: 'adgorkar', + content: 'README_4_.md', + created: '2024-10-03 23:36:57 -0400' + } + ]; + setHistory(dummyData); + }, [submissionId]); + + const table = useReactTable({ + data: history, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + return ( + +
+ + + + + + {table.getHeaderGroups().map(headerGroup => ( + + {headerGroup.headers.map(header => ( + + ))} + + ))} + + + {table.getRowModel().rows.map(row => ( + + {row.getVisibleCells().map(cell => ( + + ))} + + ))} + +
+

Submission Record

+
+ {flexRender(header.column.columnDef.header, header.getContext())} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+
+ ); +}; + +export default SubmissionHistoryView; \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx index 296f3759..90842088 100644 --- a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -102,7 +102,7 @@ const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void id: 'history', header: () => 'History', cell: (info) => ( - History + History ), enableSorting: false, enableColumnFilter: false, From 947576862072aa69d986db1c8c737ab11c3750ac Mon Sep 17 00:00:00 2001 From: mjfeng Date: Tue, 29 Oct 2024 15:09:50 -0400 Subject: [PATCH 14/19] submission history tests --- .../SubmissionHistoryView.test.tsx | 81 +++++++++++++++++++ .../SubmissionHistoryView.test.tsx.wip | 1 - 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 src/pages/Submissions/SubmissionHistoryView.test.tsx delete mode 100644 src/pages/Submissions/SubmissionHistoryView.test.tsx.wip diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx b/src/pages/Submissions/SubmissionHistoryView.test.tsx new file mode 100644 index 00000000..11b1c55b --- /dev/null +++ b/src/pages/Submissions/SubmissionHistoryView.test.tsx @@ -0,0 +1,81 @@ +// SubmissionHistoryView.test.tsx +import '@testing-library/jest-dom'; +import { render, screen, within } from '@testing-library/react'; +import { BrowserRouter } from 'react-router-dom'; +import SubmissionHistoryView from './SubmissionHistoryView'; + +// Mock useParams with different submission IDs for testing +const mockUseParams = jest.fn(); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: () => mockUseParams() +})); + +const renderWithRouter = (component: React.ReactNode) => { + return render( + + {component} + + ); +}; + +describe('SubmissionHistoryView', () => { + beforeEach(() => { + // Reset the mock before each test + mockUseParams.mockReset(); + // Default mock return value + mockUseParams.mockReturnValue({ submissionId: '38121' }); + }); + + // Add this new test + test('receives correct submission ID from URL parameters', () => { + // Set up mock to return a specific submission ID + mockUseParams.mockReturnValue({ submissionId: '12345' }); + + renderWithRouter(); + + // Verify that the mock was called + expect(mockUseParams).toHaveBeenCalled(); + + // Get the value that useParams returned + const { submissionId } = mockUseParams(); + expect(submissionId).toBe('12345'); + }); + + test('renders submission record title', () => { + renderWithRouter(); + expect(screen.getByText('Submission Record')).toBeInTheDocument(); + }); + + test('renders table headers', () => { + renderWithRouter(); + expect(screen.getByText('Team Id')).toBeInTheDocument(); + expect(screen.getByText('Operation')).toBeInTheDocument(); + expect(screen.getByText('User')).toBeInTheDocument(); + expect(screen.getByText('Content')).toBeInTheDocument(); + expect(screen.getByText('Created')).toBeInTheDocument(); + }); + + test('displays dummy data correctly', () => { + renderWithRouter(); + + // Get all rows (excluding header rows) + const rows = screen.getAllByRole('row').slice(2); // Skip title and header rows + + // Test first row data + const firstRow = rows[0]; + const cells = within(firstRow).getAllByRole('cell'); + + expect(cells[0]).toHaveTextContent('38121'); + expect(cells[1]).toHaveTextContent('Submit Hyperlink'); + expect(cells[2]).toHaveTextContent('adgorkar'); + expect(cells[3]).toHaveTextContent('https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2'); + expect(cells[4]).toHaveTextContent('2024-09-17 22:38:09 -0400'); + }); + + test('renders correct number of rows', () => { + renderWithRouter(); + const rows = screen.getAllByRole('row'); + expect(rows.length).toBe(7); // 5 data rows + 2 header rows + }); +}); \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip b/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip deleted file mode 100644 index 5040fd93..00000000 --- a/src/pages/Submissions/SubmissionHistoryView.test.tsx.wip +++ /dev/null @@ -1 +0,0 @@ -// WIP \ No newline at end of file From 25f7d0942051c008319f8887f2dad10aab7ae521 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Tue, 29 Oct 2024 18:16:57 -0400 Subject: [PATCH 15/19] remove user link --- src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx index 90842088..373bdb0f 100644 --- a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -48,9 +48,10 @@ const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void cell: (info) => info.getValue().map((member) => (
- + {/* This can be used to link to the users profile once the profile component exists */} + {/* */} {member.name} (Student {member.id}) - + {/* */}
)), size: 35, From c79574197c914480c0c86238465baba2b95e5a27 Mon Sep 17 00:00:00 2001 From: mjfeng Date: Tue, 29 Oct 2024 19:31:40 -0400 Subject: [PATCH 16/19] test changes --- .../SubmissionHistoryView.test.tsx | 39 +++++++------------ .../Submissions/SubmissionHistoryView.tsx | 32 +++++++-------- 2 files changed, 31 insertions(+), 40 deletions(-) diff --git a/src/pages/Submissions/SubmissionHistoryView.test.tsx b/src/pages/Submissions/SubmissionHistoryView.test.tsx index 11b1c55b..e85b8f21 100644 --- a/src/pages/Submissions/SubmissionHistoryView.test.tsx +++ b/src/pages/Submissions/SubmissionHistoryView.test.tsx @@ -1,10 +1,8 @@ -// SubmissionHistoryView.test.tsx import '@testing-library/jest-dom'; import { render, screen, within } from '@testing-library/react'; import { BrowserRouter } from 'react-router-dom'; import SubmissionHistoryView from './SubmissionHistoryView'; -// Mock useParams with different submission IDs for testing const mockUseParams = jest.fn(); jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -21,25 +19,17 @@ const renderWithRouter = (component: React.ReactNode) => { describe('SubmissionHistoryView', () => { beforeEach(() => { - // Reset the mock before each test mockUseParams.mockReset(); - // Default mock return value - mockUseParams.mockReturnValue({ submissionId: '38121' }); + mockUseParams.mockReturnValue({ submissionId: '1' }); }); - // Add this new test + // Check if Submission ID is correct test('receives correct submission ID from URL parameters', () => { - // Set up mock to return a specific submission ID - mockUseParams.mockReturnValue({ submissionId: '12345' }); - + mockUseParams.mockReturnValue({ submissionId: '1' }); renderWithRouter(); - - // Verify that the mock was called expect(mockUseParams).toHaveBeenCalled(); - - // Get the value that useParams returned const { submissionId } = mockUseParams(); - expect(submissionId).toBe('12345'); + expect(submissionId).toBe('1'); }); test('renders submission record title', () => { @@ -47,8 +37,10 @@ describe('SubmissionHistoryView', () => { expect(screen.getByText('Submission Record')).toBeInTheDocument(); }); + // Check if table renders properly test('renders table headers', () => { renderWithRouter(); + expect(screen.getByText('Team Id')).toBeInTheDocument(); expect(screen.getByText('Operation')).toBeInTheDocument(); expect(screen.getByText('User')).toBeInTheDocument(); @@ -56,26 +48,25 @@ describe('SubmissionHistoryView', () => { expect(screen.getByText('Created')).toBeInTheDocument(); }); - test('displays dummy data correctly', () => { + // Check if data is displayed correctly + test('displays data correctly', () => { renderWithRouter(); - // Get all rows (excluding header rows) - const rows = screen.getAllByRole('row').slice(2); // Skip title and header rows - - // Test first row data + const rows = screen.getAllByRole('row').slice(2); const firstRow = rows[0]; const cells = within(firstRow).getAllByRole('cell'); - expect(cells[0]).toHaveTextContent('38121'); + expect(cells[0]).toHaveTextContent('12345'); expect(cells[1]).toHaveTextContent('Submit Hyperlink'); - expect(cells[2]).toHaveTextContent('adgorkar'); - expect(cells[3]).toHaveTextContent('https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2'); - expect(cells[4]).toHaveTextContent('2024-09-17 22:38:09 -0400'); + expect(cells[2]).toHaveTextContent('Test_User'); + expect(cells[3]).toHaveTextContent('https://github.ncsu.edu/masonhorne/reimplementation-front-end'); + expect(cells[4]).toHaveTextContent('2024-09-17 22:38:09'); }); + // Check if rows are displayed correctly test('renders correct number of rows', () => { renderWithRouter(); const rows = screen.getAllByRole('row'); - expect(rows.length).toBe(7); // 5 data rows + 2 header rows + expect(rows.length).toBe(7); }); }); \ No newline at end of file diff --git a/src/pages/Submissions/SubmissionHistoryView.tsx b/src/pages/Submissions/SubmissionHistoryView.tsx index 4f16bdf6..e0695a41 100644 --- a/src/pages/Submissions/SubmissionHistoryView.tsx +++ b/src/pages/Submissions/SubmissionHistoryView.tsx @@ -52,39 +52,39 @@ const SubmissionHistoryView = () => { useEffect(() => { const dummyData: HistoryEntry[] = [ { - teamId: 38121, + teamId: 12345, operation: 'Submit Hyperlink', - user: 'adgorkar', - content: 'https://github.ncsu.edu/adgorkar/CSC_ECE_517_Fall2024_Program_2', - created: '2024-09-17 22:38:09 -0400' + user: 'Test_User', + content: 'https://github.ncsu.edu/masonhorne/reimplementation-front-end', + created: '2024-09-17 22:38:09' }, { - teamId: 38121, + teamId: 12345, operation: 'Submit Hyperlink', - user: 'adgorkar', + user: 'Test_User', content: 'http://152.7.176.240:8080/', - created: '2024-09-27 18:32:10 -0400' + created: '2024-09-27 18:32:10' }, { - teamId: 38121, + teamId: 12345, operation: 'Submit File', - user: 'adgorkar', + user: 'Test_User', content: 'README.md', - created: '2024-09-29 17:52:24 -0400' + created: '2024-09-29 17:52:24' }, { - teamId: 38121, + teamId: 12345, operation: 'Remove File', - user: 'adgorkar', + user: 'Test_User', content: 'README.md', - created: '2024-10-03 23:36:03 -0400' + created: '2024-10-03 23:36:03' }, { - teamId: 38121, + teamId: 12345, operation: 'Submit File', - user: 'adgorkar', + user: 'Test_User', content: 'README_4_.md', - created: '2024-10-03 23:36:57 -0400' + created: '2024-10-03 23:36:57' } ]; setHistory(dummyData); From 1204358c4d3e4f47e865f4d6dcc40a787cc877ba Mon Sep 17 00:00:00 2001 From: masonhorne Date: Thu, 14 Nov 2024 15:40:43 -0500 Subject: [PATCH 17/19] update submissions and links to single column --- .../SubmissionTable/SubmissionEntry.tsx | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx index 373bdb0f..6f4b9fc8 100644 --- a/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx +++ b/src/pages/Submissions/SubmissionTable/SubmissionEntry.tsx @@ -59,45 +59,39 @@ const SubmissionEntry = ({ onGradeClick }: { onGradeClick: (id: number) => void enableColumnFilter: false, enableGlobalFilter: false, }), - - // Links column: No search, no sorting - columnHelper.accessor('links', { + // Links and File Info column: No search, no sorting + columnHelper.accessor(row => ({ links: row.links, fileInfo: row.fileInfo }), { + id: 'links', header: () => 'Links', cell: (info) => ( +
+ {info.getValue().links.map((link, idx) => ( + + ))} +
- {info.getValue().map((link, idx) => ( - - ))} -
- ), - size: 15, - enableSorting: false, - enableColumnFilter: false, - enableGlobalFilter: false, - }), - - // File Info column: No search, no sorting - columnHelper.accessor('fileInfo', { - header: () => 'File Info', - cell: (info) => ( -
- {info.getValue().map((file, idx) => ( -
-
{file.name}
-
Size: {file.size}
-
Date Modified: {file.dateModified}
+
+
Name
+
Size
+
Date Modified
+
+ {info.getValue().fileInfo.map((file, idx) => ( +
+
{file.name}
+
{file.size}
+
{file.dateModified}
))}
+
), - size: 25, + size: 40, enableSorting: false, enableColumnFilter: false, enableGlobalFilter: false, }), - // History column: Links to history pages (No search or sorting) columnHelper.display({ id: 'history', From 21fe13619e3e0858b5b33c5a61b21cc688b673f9 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Thu, 14 Nov 2024 15:51:26 -0500 Subject: [PATCH 18/19] update submissions header to be above table --- src/pages/Submissions/SubmissionsView.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/Submissions/SubmissionsView.tsx b/src/pages/Submissions/SubmissionsView.tsx index 1ed53ba5..1f595c01 100644 --- a/src/pages/Submissions/SubmissionsView.tsx +++ b/src/pages/Submissions/SubmissionsView.tsx @@ -85,11 +85,15 @@ const SubmissionView = () => { }; return ( - + - +

Submissions


+ +
+ + Filter by Assignment handleAssignmentChange(e as any)}> From 20299de6cbea009bee8762c3a53a24100d08f206 Mon Sep 17 00:00:00 2001 From: masonhorne Date: Thu, 14 Nov 2024 15:52:16 -0500 Subject: [PATCH 19/19] update submission history header --- src/pages/Submissions/SubmissionHistoryView.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Submissions/SubmissionHistoryView.tsx b/src/pages/Submissions/SubmissionHistoryView.tsx index e0695a41..f42764d2 100644 --- a/src/pages/Submissions/SubmissionHistoryView.tsx +++ b/src/pages/Submissions/SubmissionHistoryView.tsx @@ -1,12 +1,12 @@ -import { useEffect, useState } from 'react'; -import { Container } from 'react-bootstrap'; -import { useParams } from 'react-router-dom'; import { createColumnHelper, flexRender, getCoreRowModel, useReactTable, } from '@tanstack/react-table'; +import { useEffect, useState } from 'react'; +import { Container } from 'react-bootstrap'; +import { useParams } from 'react-router-dom'; interface HistoryEntry { teamId: number; @@ -103,7 +103,7 @@ const SubmissionHistoryView = () => { -

Submission Record

+

Submission History

{table.getHeaderGroups().map(headerGroup => (