Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2467. UI for View Submissions #57

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
72f3025
create submission table entry component
masonhorne Oct 13, 2024
66a7f70
create submission list component
masonhorne Oct 13, 2024
ea660c5
create submission view component
masonhorne Oct 13, 2024
bd21616
add route for submission view
masonhorne Oct 13, 2024
0d6e047
fix styling for container
masonhorne Oct 13, 2024
b5b07f8
test submission view component
masonhorne Oct 13, 2024
e235db8
test submission list component
masonhorne Oct 13, 2024
e893949
test submission entry component
masonhorne Oct 13, 2024
be613f7
move existing individual submission view to submissions directory
masonhorne Oct 13, 2024
34ff583
Merge pull request #1 from masonhorne/mjhorne2-submissionview
masonhorne Oct 18, 2024
771c84b
update view to include 23 mocked users
masonhorne Oct 19, 2024
478b817
Merge pull request #2 from masonhorne/mjhorne2-add-mocked-users
masonhorne Oct 20, 2024
1eb5d7b
submission history view
Oct 28, 2024
eb6ec5e
Revert "submission history view"
Oct 28, 2024
64975a8
woops made a mistake, changes without the updated package file
Oct 28, 2024
9475768
submission history tests
Oct 29, 2024
272ac9d
Merge pull request #3 from masonhorne/mjfeng-historyview
masonhorne Oct 29, 2024
25f7d09
remove user link
masonhorne Oct 29, 2024
6b66f4e
Merge pull request #4 from masonhorne/mjhorne2-fix-user-link
Chris-Kastritis Oct 29, 2024
c795741
test changes
Oct 29, 2024
136b9b4
Merge pull request #5 from masonhorne/mjfeng-historyview
Alderheart Oct 29, 2024
1204358
update submissions and links to single column
masonhorne Nov 14, 2024
21fe136
update submissions header to be above table
masonhorne Nov 14, 2024
20299de
update submission history header
masonhorne Nov 14, 2024
85f1faf
Merge pull request #6 from masonhorne/mjhorne2-feedback
Chris-Kastritis Nov 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 40 additions & 31 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,46 @@
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 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 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";
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([
{
Expand Down Expand Up @@ -72,9 +73,13 @@ function App() {
},
{
path: "assignments/edit/:id/viewsubmissions",
element: <ViewSubmissions />,
element: <SubmissionView />,
loader: loadAssignment,
},
{
path: "submissions/history/:submissionId",
element: <ProtectedRoute element={<SubmissionHistoryView />} leastPrivilegeRole={ROLE.TA} />,
},
{
path: "assignments/edit/:id/viewscores",
element: <ViewScores />,
Expand Down Expand Up @@ -122,6 +127,10 @@ function App() {
},
],
},
{
path: "student_tasks",
element: <ProtectedRoute element={<SubmissionsView />} leastPrivilegeRole={ROLE.TA} />,
},
{
path: "student_tasks/participants",
element: <Participants type="student_tasks" id={1} />,
Expand Down
72 changes: 72 additions & 0 deletions src/pages/Submissions/SubmissionHistoryView.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import '@testing-library/jest-dom';
import { render, screen, within } from '@testing-library/react';
import { BrowserRouter } from 'react-router-dom';
import SubmissionHistoryView from './SubmissionHistoryView';

const mockUseParams = jest.fn();
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => mockUseParams()
}));

const renderWithRouter = (component: React.ReactNode) => {
return render(
<BrowserRouter>
{component}
</BrowserRouter>
);
};

describe('SubmissionHistoryView', () => {
beforeEach(() => {
mockUseParams.mockReset();
mockUseParams.mockReturnValue({ submissionId: '1' });
});

// Check if Submission ID is correct
test('receives correct submission ID from URL parameters', () => {
mockUseParams.mockReturnValue({ submissionId: '1' });
renderWithRouter(<SubmissionHistoryView />);
expect(mockUseParams).toHaveBeenCalled();
const { submissionId } = mockUseParams();
expect(submissionId).toBe('1');
});

test('renders submission record title', () => {
renderWithRouter(<SubmissionHistoryView />);
expect(screen.getByText('Submission Record')).toBeInTheDocument();
});

// Check if table renders properly
test('renders table headers', () => {
renderWithRouter(<SubmissionHistoryView />);

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();
});

// Check if data is displayed correctly
test('displays data correctly', () => {
renderWithRouter(<SubmissionHistoryView />);

const rows = screen.getAllByRole('row').slice(2);
const firstRow = rows[0];
const cells = within(firstRow).getAllByRole('cell');

expect(cells[0]).toHaveTextContent('12345');
expect(cells[1]).toHaveTextContent('Submit Hyperlink');
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(<SubmissionHistoryView />);
const rows = screen.getAllByRole('row');
expect(rows.length).toBe(7);
});
});
136 changes: 136 additions & 0 deletions src/pages/Submissions/SubmissionHistoryView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
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;
operation: string;
user: string;
content: string;
created: string;
}

const SubmissionHistoryView = () => {
const [history, setHistory] = useState<HistoryEntry[]>([]);

// 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<HistoryEntry>();

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: 12345,
operation: 'Submit Hyperlink',
user: 'Test_User',
content: 'https://github.ncsu.edu/masonhorne/reimplementation-front-end',
created: '2024-09-17 22:38:09'
},
{
teamId: 12345,
operation: 'Submit Hyperlink',
user: 'Test_User',
content: 'http://152.7.176.240:8080/',
created: '2024-09-27 18:32:10'
},
{
teamId: 12345,
operation: 'Submit File',
user: 'Test_User',
content: 'README.md',
created: '2024-09-29 17:52:24'
},
{
teamId: 12345,
operation: 'Remove File',
user: 'Test_User',
content: 'README.md',
created: '2024-10-03 23:36:03'
},
{
teamId: 12345,
operation: 'Submit File',
user: 'Test_User',
content: 'README_4_.md',
created: '2024-10-03 23:36:57'
}
];
setHistory(dummyData);
}, [submissionId]);

const table = useReactTable({
data: history,
columns,
getCoreRowModel: getCoreRowModel(),
});

return (
<Container className="mt-4">
<div className="table-responsive">
<table className="table table-bordered table-hover">
<thead>
<tr>
<th colSpan={5} style={{ border: 'none' }}>
<h2>Submission History</h2>
</th>
</tr>
{table.getHeaderGroups().map(headerGroup => (
<tr key={headerGroup.id}>
{headerGroup.headers.map(header => (
<th key={header.id}>
{flexRender(header.column.columnDef.header, header.getContext())}
</th>
))}
</tr>
))}
</thead>
<tbody>
{table.getRowModel().rows.map(row => (
<tr key={row.id}>
{row.getVisibleCells().map(cell => (
<td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
</Container>
);
};

export default SubmissionHistoryView;
43 changes: 43 additions & 0 deletions src/pages/Submissions/SubmissionTable/SubmissionEntry.test.tsx
Original file line number Diff line number Diff line change
@@ -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(
<BrowserRouter>
<SubmissionList submissions={mockSubmissions} onGradeClick={mockOnGradeClick} />
</BrowserRouter>
);

// Check if team name is rendered correctly
expect(screen.getByText('Anonymized_Team_38121')).toBeTruthy();
});

it('calls onGradeClick when the grade button is clicked', () => {
render(
<BrowserRouter>
<SubmissionList submissions={mockSubmissions} onGradeClick={mockOnGradeClick} />
</BrowserRouter>
);

// Simulate the button click
const button = screen.getByRole('button', { name: /Assign Grade/i });
fireEvent.click(button);

expect(mockOnGradeClick).toHaveBeenCalledWith(mockSubmissions[0].id);
});
});
Loading