Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7c9e4d8
updates Assignment interfaces in interface.ts to include additional a…
mmviola1 Mar 22, 2025
340d290
Merge pull request #7 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
f7b85d1
update AssignmentFormValues interface and transformAssignmentRequest …
mmviola1 Mar 22, 2025
c599879
Merge pull request #8 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
3938d85
update initial values in AssignmentEditor.tsx to include additional A…
mmviola1 Mar 22, 2025
2efb938
Merge pull request #9 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
32bd449
Retrieve submissions data from the backend for a particilar assignmen…
mmviola1 Mar 22, 2025
716db88
Merge pull request #10 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
3598866
Update routes in App.tsx to include AssignGrades page
mmviola1 Mar 22, 2025
093b1c6
Remove imported function from App.tsx
mmviola1 Mar 22, 2025
b14bdf6
Create AssignGrades.tsx file
mmviola1 Mar 22, 2025
57ce31e
Merge pull request #11 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
7cbbe9d
Fix handleAssignGradesClick on View Submissions page to navigate to t…
mmviola1 Mar 22, 2025
cb55a17
Merge pull request #16 from harshvora7/viewsubmissions
mmviola1 Mar 22, 2025
e940604
Implement error handling for uncaught promise in ViewSubmissions.tsx
mmviola1 Mar 23, 2025
ea82ebe
Merge pull request #17 from harshvora7/viewsubmissions
mmviola1 Mar 23, 2025
b63a7f5
Add form with field validation for grade submissions in AssignGrades.tsx
mmviola1 Mar 23, 2025
9db7de8
Update loader function for Assign Grades page (in App.tsx) to load a …
mmviola1 Mar 23, 2025
4a73b51
Merge pull request #24 from harshvora7/viewsubmissions
mmviola1 Mar 23, 2025
8dbc406
assign grades page
harshvora7 Mar 25, 2025
b6dcdcf
adding test cases for assign grades
harshvora7 Mar 25, 2025
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
617 changes: 378 additions & 239 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
"@fortawesome/react-fontawesome": "^0.2.0",
"@reduxjs/toolkit": "^1.9.5",
"@tanstack/react-table": "^8.9.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@types/jest": "^27.5.2",
"@types/node": "^16.18.29",
Expand All @@ -20,7 +18,6 @@
"axios": "^1.4.0",
"bootstrap": "^5.3.3",
"chart.js": "^4.1.1",
"recharts": "^2.0.0",
"formik": "^2.2.9",
"jquery": "^3.7.1",
"jwt-decode": "^3.1.2",
Expand All @@ -34,6 +31,7 @@
"react-redux": "^8.0.5",
"react-router-dom": "^6.11.1",
"react-scripts": "^5.0.1",
"recharts": "^2.0.0",
"redux-persist": "^6.0.0",
"sass": "^1.62.1",
"save": "^2.9.0",
Expand Down Expand Up @@ -66,11 +64,14 @@
]
},
"devDependencies": {
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.2.0",
"@types/chart.js": "^2.9.41",
"@types/jquery": "^3.5.29",
"@types/jqueryui": "^1.12.21",
"@types/react-bootstrap": "^0.32.32",
"@types/react-datepicker": "^4.10.0",
"jest": "^27.5.1",
"prettier": "^2.8.7"
}
}
15 changes: 14 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import NotFound from "./router/NotFound";
import Participants from "pages/Participants/Participant";
import ParticipantEditor from "pages/Participants/ParticipantEditor";
import { loadParticipantDataRolesAndInstitutions } from "pages/Participants/participantUtil";
import { loadParticipants } from "pages/Participants/participantUtil";
import RootLayout from "layout/Root";
import UserEditor from "./pages/Users/UserEditor";
import Users from "./pages/Users/User";
Expand All @@ -40,6 +41,11 @@ 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 AssignGrades from "pages/Assignments/AssignGrades";


function App() {
const router = createBrowserRouter([
{
Expand All @@ -50,7 +56,14 @@ function App() {
{ index: true, element: <ProtectedRoute element={<Home />} /> },
{ path: "login", element: <Login /> },
{ path: "logout", element: <ProtectedRoute element={<Logout />} /> },
// Add the ViewTeamGrades route

// Assign Grades page route
{
path: "grades/view_team/:id",
element: <AssignGrades />,
loader: loadParticipants,
},

{
path: "view-team-grades",
element: <ProtectedRoute element={<ReviewTable />} />,
Expand Down
102 changes: 102 additions & 0 deletions src/pages/Assignments/AssignGrades.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// src/pages/Assignments/AssignGrades.test.tsx

import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import "@testing-library/jest-dom";
// Import your root reducer (adjust the path as needed)
import rootReducer from "../../store/rootReducer";

// Import the component under test
import AssignGrades from "./AssignGrades";

// Example: Mock window.alert so we can check if it's called
const alertMock = jest.spyOn(window, "alert").mockImplementation(() => {});

describe("AssignGrades page", () => {
// Create a single Redux store for all tests in this file
const store = configureStore({
reducer: rootReducer,
// You can optionally add a preloadedState: {...} if needed
});

afterAll(() => {
alertMock.mockRestore();
});

// TC001: Verify "Show Submission" toggle
test("TC001: Verify 'Show Submission' toggle", () => {
render(
<Provider store={store}>
<BrowserRouter>
<AssignGrades />
</BrowserRouter>
</Provider>
);

// Initially, "No Submission Available" should NOT be visible
expect(screen.queryByText("No Submission Available")).not.toBeInTheDocument();

// Click the toggle button
const toggleButton = screen.getByText("Show Submission");
fireEvent.click(toggleButton);

// Now "No Submission Available" should be visible
expect(screen.getByText("No Submission Available")).toBeInTheDocument();

// Click again to hide
const hideButton = screen.getByText("Hide Submission");
fireEvent.click(hideButton);

// It should disappear again
expect(screen.queryByText("No Submission Available")).not.toBeInTheDocument();
});

// TC002: Validate grade input
test("TC002: Validate grade input (no grade => error alert)", () => {
render(
<Provider store={store}>
<BrowserRouter>
<AssignGrades />
</BrowserRouter>
</Provider>
);

// Click 'Save' without entering a grade
const saveButton = screen.getByText("Save");
fireEvent.click(saveButton);

// Check that the alert was called with "Grade is required!" (per your code)
expect(alertMock).toHaveBeenCalledWith("Grade is required!");
});

// TC003: Test successful save
test("TC003: Test successful save (valid grade => success alert & navigate)", () => {
render(
<Provider store={store}>
<BrowserRouter>
<AssignGrades />
</BrowserRouter>
</Provider>
);

// Enter a valid grade
const gradeInput = screen.getByLabelText("Grade");
fireEvent.change(gradeInput, { target: { value: "90" } });

// Enter a comment (optional)
const commentInput = screen.getByLabelText("Comments");
fireEvent.change(commentInput, { target: { value: "Good job!" } });

// Click Save
const saveButton = screen.getByText("Save");
fireEvent.click(saveButton);

// Check alert
expect(alertMock).toHaveBeenCalledWith("Grade and comment saved successfully!");
// If you mock useNavigate, you can also check it was called with "/"
// e.g. expect(mockNavigate).toHaveBeenCalledWith("/");
});
});
117 changes: 117 additions & 0 deletions src/pages/Assignments/AssignGrades.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import React, { useState } from "react";
import { Button, Container, Row, Col, Form } from "react-bootstrap";
import { useNavigate } from "react-router-dom";

// Importing the ReviewTable (and related data)
import ReviewTable from "../ViewTeamGrades/ReviewTable";
import dummyData from "../ViewTeamGrades/Data/dummyData.json";
import dummyDataRounds from "../ViewTeamGrades/Data/heatMapData.json";

const AssignGrades: React.FC = () => {
const navigate = useNavigate();

// Toggles the submission display
const [showSubmission, setShowSubmission] = useState(false);

// Grade and Comment states
const [grade, setGrade] = useState("");
const [comment, setComment] = useState("");

const handleSave = () => {
// If grade is empty, show error and stop
if (!grade) {
alert("Grade is required!");
return;
}

// Otherwise, show success message and navigate home
alert("Grade and comment saved successfully!");
navigate("/");
};

return (
<Container fluid className="px-4">
<Row className="mt-4">
<Col className="text-center">
<h2>Summary Report for assignment: Program 1</h2>
<p>Team: asd</p>
<hr />
</Col>
</Row>

<Row>
<Col>
<Button
variant="outline-info"
size="sm"
onClick={() => setShowSubmission((prev) => !prev)}
>
{showSubmission ? "Hide Submission" : "Show Submission"}
</Button>

{showSubmission && (
<div className="mt-2">
<strong>No Submission Available</strong>
</div>
)}
</Col>
</Row>

<Row className="mt-4">
<Col>
<h3>Teammate Review</h3>
<p>toggle question list | color legend | interaction legend</p>
{dummyDataRounds && dummyDataRounds.length > 0 ? (
<ReviewTable />
) : (
<p>There are no reviews for this assignment</p>
)}
</Col>
</Row>

<Row className="mt-4">
<Col>
<h3>Grade and comment for submission</h3>

<Form.Group
controlId="gradeInput"
className="mb-3 mt-2"
style={{ maxWidth: "200px" }}
>
<Form.Label>Grade</Form.Label>
<Form.Control
type="number"
value={grade}
onChange={(e) => setGrade(e.target.value)}
placeholder="Enter numeric grade"
/>
</Form.Group>

<Form.Group
controlId="commentInput"
className="mb-3"
style={{ maxWidth: "500px" }}
>
<Form.Label>Comments</Form.Label>
<Form.Control
as="textarea"
rows={3}
value={comment}
onChange={(e) => setComment(e.target.value)}
placeholder="Enter comments"
/>
</Form.Group>

<Button variant="primary" onClick={handleSave}>
Save
</Button>
<Button variant="secondary" className="ms-2" onClick={() => navigate(-1)}>
Back
</Button>
</Col>
</Row>
</Container>
);
};

export default AssignGrades;
3 changes: 3 additions & 0 deletions src/pages/Assignments/AssignmentEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const initialValues: IAssignmentFormValues = {
has_badge: false,
staggered_deadline: false,
is_calibrated: false,
has_teams:false,
has_topics:false,
max_team_size: 1,
// Add other assignment-specific initial values
};

Expand Down
7 changes: 6 additions & 1 deletion src/pages/Assignments/AssignmentUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export interface IAssignmentFormValues {
has_badge:boolean;
staggered_deadline:boolean;
is_calibrated:boolean;
has_teams:boolean;
has_topics:boolean;
max_team_size:number;
}


Expand Down Expand Up @@ -45,7 +48,9 @@ export const transformAssignmentResponse = (assignmentResponse: string) => {
has_badge:assignment.has_badge,
staggered_deadline:assignment.staggered_deadline,
is_calibrated:assignment.is_calibrated,

has_teams:assignment.has_teams,
has_topics:assignment.has_topics,
max_team_size:assignment.max_team_size,
};
return assignmentValues;
};
Expand Down
Loading