From 286920a6e7e997733ecfb20e19c101a778d67013 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Thu, 6 Mar 2025 03:46:59 -0500 Subject: [PATCH 01/11] hosted backend and URL update --- package-lock.json | 45 ++++++++++++++++++++++++++++++++++++++++----- src/hooks/useAPI.ts | 2 +- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3ef4561e..ec5213eb 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.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "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.8", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.8.tgz", + "integrity": "sha512-IkGZlVpXP+83QpMm4uxEiGqSI7jFizwVtF3+n5Pc3k7sMO+tkd0qxh2OzLhenM0K80xtmAONWGBn082EiBQSDA==", + "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/hooks/useAPI.ts b/src/hooks/useAPI.ts index 47ba7ee5..416995c3 100644 --- a/src/hooks/useAPI.ts +++ b/src/hooks/useAPI.ts @@ -6,7 +6,7 @@ import { getAuthToken } from "../utils/auth"; * @author Ankur Mundra on April, 2023 */ -axios.defaults.baseURL = "http://localhost:3002/api/v1"; +axios.defaults.baseURL = "http://152.7.178.127:3002/api/v1"; axios.defaults.headers.common["Accept"] = "application/json"; axios.defaults.headers.post["Content-Type"] = "application/json"; axios.defaults.headers.put["Content-Type"] = "application/json"; From d098ec8082e0c4b8bfcf466cba6340a50f901f5f Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Tue, 11 Mar 2025 14:36:20 -0400 Subject: [PATCH 02/11] bug fix backend URL --- src/pages/Authentication/Login.tsx | 2 +- src/utils/axios_client.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Authentication/Login.tsx b/src/pages/Authentication/Login.tsx index 2051b297..e52d953d 100644 --- a/src/pages/Authentication/Login.tsx +++ b/src/pages/Authentication/Login.tsx @@ -30,7 +30,7 @@ const Login: React.FC = () => { const onSubmit = (values: ILoginFormValues, submitProps: FormikHelpers) => { axios - .post("http://localhost:3002/login", values) + .post("http://152.7.178.127:3002/login", values) .then((response) => { const payload = setAuthToken(response.data.token); diff --git a/src/utils/axios_client.ts b/src/utils/axios_client.ts index b5fe47d7..77c9d2cd 100644 --- a/src/utils/axios_client.ts +++ b/src/utils/axios_client.ts @@ -6,7 +6,7 @@ import { getAuthToken } from "./auth"; */ const axiosClient = axios.create({ - baseURL: "http://localhost:3002/api/v1", + baseURL: "http://152.7.178.127:3002/api/v1", timeout: 1000, headers: { "Content-Type": "application/json", From ca67c78055aa239432e1d430685f371a21cc6fad Mon Sep 17 00:00:00 2001 From: Raj <24rajpatel48@gmail.com> Date: Tue, 11 Mar 2025 18:17:39 -0700 Subject: [PATCH 03/11] Implemented Reusable componets of Heat-Grid --- src/App.tsx | 2 +- .../ViewTeamGrades => components/HeatGrid}/App.tsx | 0 .../HeatGrid}/BarGraph.tsx | 0 .../HeatGrid}/CircularProgress.tsx | 0 .../ViewTeamGrades => components/HeatGrid}/Filters.tsx | 0 .../HeatGrid}/ReviewTable.tsx | 10 +++++----- .../HeatGrid}/ReviewTableRow.tsx | 2 +- .../HeatGrid}/RoundSelector.tsx | 4 ++-- .../HeatGrid}/ShowReviews.tsx | 2 +- .../HeatGrid}/Statistics.tsx | 10 +++++----- .../HeatGrid}/teamMarks.tsx | 0 src/pages/ViewTeamGrades/utils.ts | 2 +- 12 files changed, 16 insertions(+), 16 deletions(-) rename src/{pages/ViewTeamGrades => components/HeatGrid}/App.tsx (100%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/BarGraph.tsx (100%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/CircularProgress.tsx (100%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/Filters.tsx (100%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/ReviewTable.tsx (94%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/ReviewTableRow.tsx (94%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/RoundSelector.tsx (87%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/ShowReviews.tsx (97%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/Statistics.tsx (90%) rename src/{pages/ViewTeamGrades => components/HeatGrid}/teamMarks.tsx (100%) diff --git a/src/App.tsx b/src/App.tsx index 27736ba3..1b2d5029 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -30,7 +30,7 @@ import { loadCourseInstructorDataAndInstitutions } from "pages/Courses/CourseUti 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 ReviewTable from "./components/HeatGrid/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"; diff --git a/src/pages/ViewTeamGrades/App.tsx b/src/components/HeatGrid/App.tsx similarity index 100% rename from src/pages/ViewTeamGrades/App.tsx rename to src/components/HeatGrid/App.tsx diff --git a/src/pages/ViewTeamGrades/BarGraph.tsx b/src/components/HeatGrid/BarGraph.tsx similarity index 100% rename from src/pages/ViewTeamGrades/BarGraph.tsx rename to src/components/HeatGrid/BarGraph.tsx diff --git a/src/pages/ViewTeamGrades/CircularProgress.tsx b/src/components/HeatGrid/CircularProgress.tsx similarity index 100% rename from src/pages/ViewTeamGrades/CircularProgress.tsx rename to src/components/HeatGrid/CircularProgress.tsx diff --git a/src/pages/ViewTeamGrades/Filters.tsx b/src/components/HeatGrid/Filters.tsx similarity index 100% rename from src/pages/ViewTeamGrades/Filters.tsx rename to src/components/HeatGrid/Filters.tsx diff --git a/src/pages/ViewTeamGrades/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx similarity index 94% rename from src/pages/ViewTeamGrades/ReviewTable.tsx rename to src/components/HeatGrid/ReviewTable.tsx index 5aa18db7..7804e148 100644 --- a/src/pages/ViewTeamGrades/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -1,15 +1,15 @@ import React, { useEffect, useState } from "react"; import ReviewTableRow from "./ReviewTableRow"; import RoundSelector from "./RoundSelector"; -import dummyDataRounds from "./Data/heatMapData.json"; -import dummyData from "./Data/dummyData.json"; -import { calculateAverages, getColorClass } from "./utils"; -import "./grades.scss"; +import dummyDataRounds from "../../pages/ViewTeamGrades/Data/heatMapData.json"; +import dummyData from "../../pages/ViewTeamGrades/Data/dummyData.json"; +import { calculateAverages, getColorClass } from "../../pages/ViewTeamGrades/utils"; +import "../../pages/ViewTeamGrades/grades.scss"; import { Link } from "react-router-dom"; import Statistics from "./Statistics"; import Filters from "./Filters"; import ShowReviews from "./ShowReviews"; //importing show reviews component -import dummyauthorfeedback from "./Data/authorFeedback.json"; // Importing dummy data for author feedback +import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; // Importing dummy data for author feedback const ReviewTable: React.FC = () => { const [currentRound, setCurrentRound] = useState(-1); diff --git a/src/pages/ViewTeamGrades/ReviewTableRow.tsx b/src/components/HeatGrid/ReviewTableRow.tsx similarity index 94% rename from src/pages/ViewTeamGrades/ReviewTableRow.tsx rename to src/components/HeatGrid/ReviewTableRow.tsx index ffa4e5b6..e9f1bf56 100644 --- a/src/pages/ViewTeamGrades/ReviewTableRow.tsx +++ b/src/components/HeatGrid/ReviewTableRow.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { getColorClass } from "./utils"; // Importing utility functions +import { getColorClass } from "../../pages/ViewTeamGrades/utils"; // Importing utility functions import { ReviewData } from "./App"; // Importing the ReviewData interface from App // Props interface for ReviewTableRow component diff --git a/src/pages/ViewTeamGrades/RoundSelector.tsx b/src/components/HeatGrid/RoundSelector.tsx similarity index 87% rename from src/pages/ViewTeamGrades/RoundSelector.tsx rename to src/components/HeatGrid/RoundSelector.tsx index 103362d1..025b8893 100644 --- a/src/pages/ViewTeamGrades/RoundSelector.tsx +++ b/src/components/HeatGrid/RoundSelector.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from "react"; -import dummyDataRounds from "./Data/heatMapData.json"; -import teamData from "./Data/dummyData.json"; +import dummyDataRounds from "../../pages/ViewTeamGrades/Data/heatMapData.json"; +import teamData from "../../pages/ViewTeamGrades/Data/dummyData.json"; interface RoundSelectorProps { currentRound: number; diff --git a/src/pages/ViewTeamGrades/ShowReviews.tsx b/src/components/HeatGrid/ShowReviews.tsx similarity index 97% rename from src/pages/ViewTeamGrades/ShowReviews.tsx rename to src/components/HeatGrid/ShowReviews.tsx index 570a412e..a3786e44 100644 --- a/src/pages/ViewTeamGrades/ShowReviews.tsx +++ b/src/components/HeatGrid/ShowReviews.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { getColorClass } from "./utils"; +import { getColorClass } from "../../pages/ViewTeamGrades/utils"; import { RootState } from "../../store/store"; import { useDispatch, useSelector } from "react-redux"; diff --git a/src/pages/ViewTeamGrades/Statistics.tsx b/src/components/HeatGrid/Statistics.tsx similarity index 90% rename from src/pages/ViewTeamGrades/Statistics.tsx rename to src/components/HeatGrid/Statistics.tsx index e26d175a..34be5b0f 100644 --- a/src/pages/ViewTeamGrades/Statistics.tsx +++ b/src/components/HeatGrid/Statistics.tsx @@ -1,10 +1,10 @@ // Statistics.tsx import React, { useState, useEffect } from "react"; -import { calculateAverages } from "./utils"; -import "./grades.scss"; -import dummyDataRounds from "./Data/heatMapData.json"; // Importing dummy data for rounds -import dummyauthorfeedback from "./Data/authorFeedback.json"; // Importing dummy data for author feedback -import teammateData from "./Data/teammateData.json"; +import { calculateAverages } from "../../pages/ViewTeamGrades/utils"; +import "../../pages/ViewTeamGrades/grades.scss"; +import dummyDataRounds from "../../pages/ViewTeamGrades/Data/heatMapData.json"; // Importing dummy data for rounds +import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; // Importing dummy data for author feedback +import teammateData from "../../pages/ViewTeamGrades/Data/teammateData.json"; //props for statistics component interface StatisticsProps {} diff --git a/src/pages/ViewTeamGrades/teamMarks.tsx b/src/components/HeatGrid/teamMarks.tsx similarity index 100% rename from src/pages/ViewTeamGrades/teamMarks.tsx rename to src/components/HeatGrid/teamMarks.tsx diff --git a/src/pages/ViewTeamGrades/utils.ts b/src/pages/ViewTeamGrades/utils.ts index 8cf8d2a2..ada25a47 100644 --- a/src/pages/ViewTeamGrades/utils.ts +++ b/src/pages/ViewTeamGrades/utils.ts @@ -1,4 +1,4 @@ -import { ReviewData } from './App'; +import { ReviewData } from '../../components/HeatGrid/App'; // Function to get color class based on score and maxScore export const getColorClass = (score: number, maxScore: number) => { From db00382e272987d751973d4aa2d0bdc5f92e5b9a Mon Sep 17 00:00:00 2001 From: Parth Kulkarni Date: Sat, 15 Mar 2025 18:25:40 -0400 Subject: [PATCH 04/11] Show anonymized review headers for students and actual names for others --- src/components/HeatGrid/ReviewTable.tsx | 33 ++++++++++++++++++++----- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index 7804e148..c88893ea 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -11,7 +11,26 @@ import Filters from "./Filters"; import ShowReviews from "./ShowReviews"; //importing show reviews component import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; // Importing dummy data for author feedback -const ReviewTable: React.FC = () => { +interface Review { + name: string; + score: number; + comment?: string; +} + +interface RoundData { + questionNumber: string; + questionText: string; + reviews: Review[]; + RowAvg: number; + maxScore: number; +} + +interface ReviewTableProps { + currentUser?: { id: string }; + project?: { student: { id: string } }; +} + +const ReviewTable: React.FC = ({ currentUser, project }) => { const [currentRound, setCurrentRound] = useState(-1); const [sortOrderRow, setSortOrderRow] = useState<"asc" | "desc" | "none">("none"); const [showToggleQuestion, setShowToggleQuestion] = useState(false); @@ -54,7 +73,7 @@ const ReviewTable: React.FC = () => { setShowToggleQuestion(!showToggleQuestion); }; - const renderTable = (roundData: any, roundIndex: number) => { + const renderTable = (roundData: RoundData[], roundIndex: number) => { const { averagePeerReviewScore, columnAverages, sortedData } = calculateAverages( roundData, sortOrderRow @@ -76,10 +95,12 @@ const ReviewTable: React.FC = () => { Question )} - {Array.from({ length: roundData[0].reviews.length }, (_, i) => ( - {`Review ${ - i + 1 - }`} + {roundData[0]?.reviews?.map((review: Review, index: number) => ( + + {currentUser?.id === project?.student?.id + ? `Review ${index + 1}` + : review.name} + ))} Average From 1af79d7a864bac8f1b98b8eaf8cd1d8f3e13d9ea Mon Sep 17 00:00:00 2001 From: Parth Kulkarni Date: Fri, 21 Mar 2025 19:10:37 -0400 Subject: [PATCH 05/11] Changed toggleQuestion checkbox to make it a dynamic link --- src/components/HeatGrid/ReviewTable.tsx | 27 ++++++++++++------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index c88893ea..12c21c65 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -81,9 +81,19 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { return (
-

- Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) -

+
+

+ Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) +

+ + {showToggleQuestion ? "Hide Question List" : "Show Question List"} + +
+ @@ -186,17 +196,6 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { -
- - -
- {/* Conditionally render tables based on currentRound */} {currentRound === -1 ? dummyDataRounds.map((roundData, index) => renderTable(roundData, index)) // Render a table for each round if "All Rounds" is selected From 24cba7f3353a597e6aed4a67e89e6e0fb1c41c4c Mon Sep 17 00:00:00 2001 From: Parth Kulkarni Date: Sun, 23 Mar 2025 17:30:37 -0400 Subject: [PATCH 06/11] Added color and interaction legend which is has hovering enabled. --- src/components/HeatGrid/ReviewTable.tsx | 30 +++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index 12c21c65..a926157a 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -10,6 +10,7 @@ import Statistics from "./Statistics"; import Filters from "./Filters"; import ShowReviews from "./ShowReviews"; //importing show reviews component import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; // Importing dummy data for author feedback +import ToolTip from "components/ToolTip"; interface Review { name: string; @@ -80,18 +81,33 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { ); return ( -
-
-

- Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) -

+
+

+ Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) +

+
From bdf8f0371bece97f0b47f8bbf2e4d9627f05e262 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Mon, 24 Mar 2025 16:31:03 -0400 Subject: [PATCH 07/11] feat: word count for reviews --- src/components/HeatGrid/ReviewTable.tsx | 30 +++++------------- src/components/HeatGrid/ReviewTableRow.tsx | 36 ++++++++++++++++++++-- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index a926157a..822bacb7 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -79,13 +79,13 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { roundData, sortOrderRow ); - + return (

Review (Round: {roundIndex + 1} of {dummyDataRounds.length})

-
{/* USE gap-4 for EVEN Spacing */} + - +
@@ -121,13 +106,14 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { Question )} - {roundData[0]?.reviews?.map((review: Review, index: number) => ( + {roundData[0]?.reviews?.map((_, index) => ( ))} + {/* Question Number */} @@ -23,6 +47,7 @@ const ReviewTableRow: React.FC = ({ row, showToggleQuestion       {row.questionNumber} + {/* Toggle Question */} {showToggleQuestion && } @@ -41,10 +66,17 @@ const ReviewTableRow: React.FC = ({ row, showToggleQuestion ))} + {/* Short and Long Comments Count */} + + {/* Row Average */} ); }; -export default ReviewTableRow; // Exporting the ReviewTableRow component as default +export default ReviewTableRow; // Exporting the ReviewTableRow component as default \ No newline at end of file From 45c99b299e9e895253b667cfbc9b49fd319b1e35 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Mon, 24 Mar 2025 20:56:54 -0400 Subject: [PATCH 08/11] issue 7 from wiki --- src/components/HeatGrid/ReviewTable.tsx | 195 ++++++++++++++---------- 1 file changed, 111 insertions(+), 84 deletions(-) diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index 822bacb7..06bd7498 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -3,14 +3,15 @@ import ReviewTableRow from "./ReviewTableRow"; import RoundSelector from "./RoundSelector"; import dummyDataRounds from "../../pages/ViewTeamGrades/Data/heatMapData.json"; import dummyData from "../../pages/ViewTeamGrades/Data/dummyData.json"; -import { calculateAverages, getColorClass } from "../../pages/ViewTeamGrades/utils"; +import { calculateAverages } from "../../pages/ViewTeamGrades/utils"; import "../../pages/ViewTeamGrades/grades.scss"; import { Link } from "react-router-dom"; import Statistics from "./Statistics"; import Filters from "./Filters"; -import ShowReviews from "./ShowReviews"; //importing show reviews component -import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; // Importing dummy data for author feedback +import ShowReviews from "./ShowReviews"; +import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; import ToolTip from "components/ToolTip"; +import { Button } from "react-bootstrap"; interface Review { name: string; @@ -27,15 +28,15 @@ interface RoundData { } interface ReviewTableProps { - currentUser?: { id: string }; - project?: { student: { id: string } }; + currentUser?: { id: string }; + project?: { student: { id: string } }; } const ReviewTable: React.FC = ({ currentUser, project }) => { const [currentRound, setCurrentRound] = useState(-1); const [sortOrderRow, setSortOrderRow] = useState<"asc" | "desc" | "none">("none"); + const [sortByTotalScore, setSortByTotalScore] = useState<"asc" | "desc" | "none">("none"); const [showToggleQuestion, setShowToggleQuestion] = useState(false); - const [open, setOpen] = useState(false); const [teamMembers, setTeamMembers] = useState([]); const [showReviews, setShowReviews] = useState(false); const [ShowAuthorFeedback, setShowAuthorFeedback] = useState(false); @@ -46,55 +47,75 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { }, []); const toggleSortOrderRow = () => { - setSortOrderRow((prevSortOrder) => { - if (prevSortOrder === "asc") return "desc"; - if (prevSortOrder === "desc") return "none"; - return "asc"; - }); - }; - - const toggleShowReviews = () => { - setShowReviews((prev) => !prev); - }; - - const selectRound = (r: number) => { - setRoundSelected((prev) => r); - }; - - // Function to toggle the visibility of ShowAuthorFeedback component - const toggleAuthorFeedback = () => { - setShowAuthorFeedback((prev) => !prev); + setSortOrderRow((prev) => + prev === "asc" ? "desc" : prev === "desc" ? "none" : "asc" + ); }; - const handleRoundChange = (roundIndex: number) => { - setCurrentRound(roundIndex); + const toggleSortByTotalScore = () => { + setSortByTotalScore((prev) => + prev === "asc" ? "desc" : prev === "desc" ? "none" : "asc" + ); }; - const toggleShowQuestion = () => { - setShowToggleQuestion(!showToggleQuestion); - }; + const toggleShowReviews = () => setShowReviews((prev) => !prev); + const toggleAuthorFeedback = () => setShowAuthorFeedback((prev) => !prev); + const selectRound = (r: number) => setRoundSelected(r); + const toggleShowQuestion = () => setShowToggleQuestion(!showToggleQuestion); + const handleRoundChange = (roundIndex: number) => setCurrentRound(roundIndex); const renderTable = (roundData: RoundData[], roundIndex: number) => { const { averagePeerReviewScore, columnAverages, sortedData } = calculateAverages( roundData, sortOrderRow ); - + + let displayData = [...sortedData]; + + if (sortByTotalScore !== "none") { + displayData.sort((a, b) => { + const totalA = a.reviews.reduce((sum, r) => sum + r.score, 0); + const totalB = b.reviews.reduce((sum, r) => sum + r.score, 0); + return sortByTotalScore === "asc" ? totalA - totalB : totalB - totalA; + }); + } + return ( -
-

- Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) -

-
- - {showToggleQuestion ? "toggle question list" : "toggle question list"} - +
+
+

+ Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) +

+
+ + {showToggleQuestion ? "Hide Questions" : "Show Questions"} + + + Hide Tags + + + Color Legend{" "} + + + + Interaction Legend{" "} + + +
- +
- {currentUser?.id === project?.student?.id - ? `Review ${index + 1}` - : review.name} + Review {index + 1} + Word Count + Average {sortOrderRow === "none" && ▲▼} diff --git a/src/components/HeatGrid/ReviewTableRow.tsx b/src/components/HeatGrid/ReviewTableRow.tsx index e9f1bf56..d828e724 100644 --- a/src/components/HeatGrid/ReviewTableRow.tsx +++ b/src/components/HeatGrid/ReviewTableRow.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React from "react"; import { getColorClass } from "../../pages/ViewTeamGrades/utils"; // Importing utility functions import { ReviewData } from "./App"; // Importing the ReviewData interface from App @@ -8,8 +8,32 @@ interface ReviewTableRowProps { showToggleQuestion: boolean; // Flag to toggle the question column } +// Helper function to calculate word count +const calculateWordCount = (comment?: string) => { + return comment ? comment.split(/\s+/).filter((word) => word).length : 0; +}; + +// Helper function to count short and long comments +const countShortAndLongComments = (reviews: { comment?: string }[]) => { + let shortCount = 0; + let longCount = 0; + + reviews.forEach((review) => { + const wordCount = calculateWordCount(review.comment); + if (wordCount > 20) { + longCount++; + } else if (wordCount <= 10) { + shortCount++; + } + }); + + return { shortCount, longCount }; +}; + // Functional component ReviewTableRow const ReviewTableRow: React.FC = ({ row, showToggleQuestion }) => { + const { shortCount, longCount } = countShortAndLongComments(row.reviews); + return (
{row.questionText} + {longCount > 0 && `${longCount} Long`} + {longCount > 0 && shortCount > 0 && ", "} + {shortCount > 0 && `${shortCount} Short`} + {row.RowAvg.toFixed(2)}
@@ -106,30 +127,30 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { Question )} - {roundData[0]?.reviews?.map((_, index) => ( + {roundData[0]?.reviews?.map((review, index) => ( ))} - - {sortedData.map((row, index) => ( + {displayData.map((row, index) => ( ))} - + {showToggleQuestion && } {columnAverages.map((avg, index) => (
- Review {index + 1} + {currentUser?.id === project?.student?.id + ? `Review ${index + 1}` + : review.name} + Word Count Average - {sortOrderRow === "none" && ▲▼} + {sortOrderRow === "none" && ▲▼} {sortOrderRow === "asc" && } {sortOrderRow === "desc" && }
- Avg - Avg @@ -139,12 +160,22 @@ const ReviewTable: React.FC = ({ currentUser, project }) => {
-
-
- Average peer review score:{" "} - {averagePeerReviewScore} -
-
+ +
+ +
+ +
+
+ Average peer review score:{" "} + {averagePeerReviewScore} +
+
); }; @@ -162,6 +193,7 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { ))} +
Submission Links
    @@ -193,40 +225,35 @@ const ReviewTable: React.FC = ({ currentUser, project }) => {
-
- {/* Conditionally render tables based on currentRound */} {currentRound === -1 - ? dummyDataRounds.map((roundData, index) => renderTable(roundData, index)) // Render a table for each round if "All Rounds" is selected + ? dummyDataRounds.map((roundData, index) => renderTable(roundData, index)) : renderTable(dummyDataRounds[currentRound], currentRound)} -
- -
+ -
- {showReviews && ( -
-

Reviews

- -
- )} - {ShowAuthorFeedback && ( -
-

Author Feedback

- -
- )} -
+ {showReviews && ( +
+

Reviews

+ +
+ )} + + {ShowAuthorFeedback && ( +
+

Author Feedback

+ +
+ )} -

+

Grade and comment for submission

Grade: {dummyData.grade}
@@ -234,11 +261,11 @@ const ReviewTable: React.FC = ({ currentUser, project }) => {
Late Penalty: {dummyData.late_penalty}
-

+
Back ); }; -export default ReviewTable; +export default ReviewTable; \ No newline at end of file From 17b90ab9f2beb2e54244f8534ecaef6549c0da74 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Mon, 24 Mar 2025 21:53:47 -0400 Subject: [PATCH 09/11] added button and fixes --- src/components/HeatGrid/ReviewTable.tsx | 168 ++++------------- .../HeatGrid/ReviewTableContent.tsx | 174 ++++++++++++++++++ src/components/HeatGrid/ReviewTableRow.tsx | 21 ++- src/components/HeatGrid/types.ts | 5 + 4 files changed, 223 insertions(+), 145 deletions(-) create mode 100644 src/components/HeatGrid/ReviewTableContent.tsx create mode 100644 src/components/HeatGrid/types.ts diff --git a/src/components/HeatGrid/ReviewTable.tsx b/src/components/HeatGrid/ReviewTable.tsx index 06bd7498..7c7cb2f7 100644 --- a/src/components/HeatGrid/ReviewTable.tsx +++ b/src/components/HeatGrid/ReviewTable.tsx @@ -1,31 +1,14 @@ import React, { useEffect, useState } from "react"; -import ReviewTableRow from "./ReviewTableRow"; import RoundSelector from "./RoundSelector"; import dummyDataRounds from "../../pages/ViewTeamGrades/Data/heatMapData.json"; import dummyData from "../../pages/ViewTeamGrades/Data/dummyData.json"; -import { calculateAverages } from "../../pages/ViewTeamGrades/utils"; import "../../pages/ViewTeamGrades/grades.scss"; import { Link } from "react-router-dom"; import Statistics from "./Statistics"; import Filters from "./Filters"; import ShowReviews from "./ShowReviews"; import dummyauthorfeedback from "../../pages/ViewTeamGrades/Data/authorFeedback.json"; -import ToolTip from "components/ToolTip"; -import { Button } from "react-bootstrap"; - -interface Review { - name: string; - score: number; - comment?: string; -} - -interface RoundData { - questionNumber: string; - questionText: string; - reviews: Review[]; - RowAvg: number; - maxScore: number; -} +import ReviewTableContent from "./ReviewTableContent"; interface ReviewTableProps { currentUser?: { id: string }; @@ -64,122 +47,6 @@ const ReviewTable: React.FC = ({ currentUser, project }) => { const toggleShowQuestion = () => setShowToggleQuestion(!showToggleQuestion); const handleRoundChange = (roundIndex: number) => setCurrentRound(roundIndex); - const renderTable = (roundData: RoundData[], roundIndex: number) => { - const { averagePeerReviewScore, columnAverages, sortedData } = calculateAverages( - roundData, - sortOrderRow - ); - - let displayData = [...sortedData]; - - if (sortByTotalScore !== "none") { - displayData.sort((a, b) => { - const totalA = a.reviews.reduce((sum, r) => sum + r.score, 0); - const totalB = b.reviews.reduce((sum, r) => sum + r.score, 0); - return sortByTotalScore === "asc" ? totalA - totalB : totalB - totalA; - }); - } - - return ( -
-
-

- Review (Round: {roundIndex + 1} of {dummyDataRounds.length}) -

-
- - {showToggleQuestion ? "Hide Questions" : "Show Questions"} - - - Hide Tags - - - Color Legend{" "} - - - - Interaction Legend{" "} - - -
-
- - - - - - {showToggleQuestion && ( - - )} - {roundData[0]?.reviews?.map((review, index) => ( - - ))} - - - - - - {displayData.map((row, index) => ( - - ))} - - - {showToggleQuestion && } - {columnAverages.map((avg, index) => ( - - ))} - - -
- Question No. - - Question - - {currentUser?.id === project?.student?.id - ? `Review ${index + 1}` - : review.name} - - Word Count - - Average - {sortOrderRow === "none" && ▲▼} - {sortOrderRow === "asc" && } - {sortOrderRow === "desc" && } -
Avg - {avg.toFixed(2)} -
- -
- -
- -
-
- Average peer review score:{" "} - {averagePeerReviewScore} -
-
-
- ); - }; - return (

Summary Report: Program 2

@@ -228,10 +95,39 @@ const ReviewTable: React.FC = ({ currentUser, project }) => {
+ + {/* Render Round(s) */} {currentRound === -1 - ? dummyDataRounds.map((roundData, index) => renderTable(roundData, index)) - : renderTable(dummyDataRounds[currentRound], currentRound)} + ? dummyDataRounds.map((roundData, index) => ( + + )) + : ( + + )} void; + sortByTotalScore: "asc" | "desc" | "none"; + toggleSortByTotalScore: () => void; + toggleShowQuestion: () => void; + showToggleQuestion: boolean; +} + +const ReviewTableContent: React.FC = ({ + roundData, + roundIndex, + currentUser, + project, + sortOrderRow, + toggleSortOrderRow, + sortByTotalScore, + toggleSortByTotalScore, + showToggleQuestion, + toggleShowQuestion +}) => { + const { averagePeerReviewScore, columnAverages, sortedData } = calculateAverages( + roundData, + sortOrderRow + ); + const [showWordComments, setshowWordComments] = useState(0); + + let displayData = [...sortedData]; + if (sortByTotalScore !== "none") { + displayData.sort((a, b) => { + const totalA = a.reviews.reduce((sum, r) => sum + r.score, 0); + const totalB = b.reviews.reduce((sum, r) => sum + r.score, 0); + return sortByTotalScore === "asc" ? totalA - totalB : totalB - totalA; + }); + } + + return ( +
+
+

Review (Round: {roundIndex + 1})

+
+ + +
+
{/* USE gap-4 for EVEN Spacing */} + + {showToggleQuestion ? "toggle question list" : "toggle question list"} + + + + hide tags + + + + color legend + + + + interaction legend + +
+ +
+ + + + + + {showToggleQuestion && ( + + )} + {roundData[0]?.reviews?.map((review, index) => ( + + ))} + {showWordComments != 0 && ( + + )} + + + + + {displayData.map((row, index) => ( + + ))} + + + {showToggleQuestion && } + {columnAverages.map((avg, index) => ( + + ))} + + +
Question No.Question + {currentUser?.id === project?.student?.id + ? `Review ${index + 1}` + : review.name} + + Word Count + + Average + {sortOrderRow === "none" && ▲▼} + {sortOrderRow === "asc" && } + {sortOrderRow === "desc" && } +
Avg + {avg.toFixed(2)} +
+
+ +
+
+
+ Average peer review score:{" "} + {averagePeerReviewScore} +
+
+
+ ); +}; + +export default ReviewTableContent; diff --git a/src/components/HeatGrid/ReviewTableRow.tsx b/src/components/HeatGrid/ReviewTableRow.tsx index d828e724..39225b4f 100644 --- a/src/components/HeatGrid/ReviewTableRow.tsx +++ b/src/components/HeatGrid/ReviewTableRow.tsx @@ -4,8 +4,9 @@ import { ReviewData } from "./App"; // Importing the ReviewData interface from A // Props interface for ReviewTableRow component interface ReviewTableRowProps { - row: ReviewData; // Data for the row - showToggleQuestion: boolean; // Flag to toggle the question column + row: ReviewData; + showToggleQuestion: boolean; + showWordCount: number; // NEW } // Helper function to calculate word count @@ -31,7 +32,7 @@ const countShortAndLongComments = (reviews: { comment?: string }[]) => { }; // Functional component ReviewTableRow -const ReviewTableRow: React.FC = ({ row, showToggleQuestion }) => { +const ReviewTableRow: React.FC = ({ row, showToggleQuestion , showWordCount }) => { const { shortCount, longCount } = countShortAndLongComments(row.reviews); return ( @@ -66,12 +67,14 @@ const ReviewTableRow: React.FC = ({ row, showToggleQuestion ))} - {/* Short and Long Comments Count */} - - {longCount > 0 && `${longCount} Long`} - {longCount > 0 && shortCount > 0 && ", "} - {shortCount > 0 && `${shortCount} Short`} - + {showWordCount !=0 && ( + + {showWordCount >= 20 && `${longCount}`} + {showWordCount <= 10 && `${shortCount}`} + + )} + + {/* Row Average */} {row.RowAvg.toFixed(2)} diff --git a/src/components/HeatGrid/types.ts b/src/components/HeatGrid/types.ts new file mode 100644 index 00000000..5069631e --- /dev/null +++ b/src/components/HeatGrid/types.ts @@ -0,0 +1,5 @@ +export interface Review { + name: string; + score: number; + comment?: string; + } \ No newline at end of file From 743215239006c6f954380c7379cf5e42b3de6e25 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Mon, 24 Mar 2025 22:09:48 -0400 Subject: [PATCH 10/11] final push --- src/components/HeatGrid/ReviewTableContent.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/HeatGrid/ReviewTableContent.tsx b/src/components/HeatGrid/ReviewTableContent.tsx index f9e82b66..fd7ff1ed 100644 --- a/src/components/HeatGrid/ReviewTableContent.tsx +++ b/src/components/HeatGrid/ReviewTableContent.tsx @@ -160,6 +160,13 @@ const ReviewTableContent: React.FC = ({ > Sort by Total Review Score ({sortByTotalScore === "none" ? "Off" : sortByTotalScore}) + Teamates: + {["Raj Patel", "Aditya Pai", "Parth Kulkarni"].map((teammate, index) => ( + + {teammate} + + ))} +
From e5a4842f3ce3244f4fa75c9ec625b6cecc220a19 Mon Sep 17 00:00:00 2001 From: adityapai18 Date: Mon, 24 Mar 2025 22:39:43 -0400 Subject: [PATCH 11/11] tests added --- src/App.test.tsx | 66 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/App.test.tsx b/src/App.test.tsx index 2a68616d..a2973e6a 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -1,5 +1,8 @@ import React from 'react'; -import { render, screen } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { MemoryRouter, Route, Routes } from "react-router-dom"; +import ReviewTable from 'components/HeatGrid/ReviewTable'; + import App from './App'; test('renders learn react link', () => { @@ -7,3 +10,64 @@ test('renders learn react link', () => { const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); }); + + +describe("ReviewTable page", () => { + const setup = () => + render( + + + } /> + + + ); + + test("renders summary report and team info", async () => { + setup(); + expect(screen.getByText(/Summary Report: Program 2/i)).toBeInTheDocument(); + expect(screen.getByText(/Team:/i)).toBeInTheDocument(); + }); + + test("renders submission links", async () => { + setup(); + expect( + screen.getByText("https://github.ncsu.edu/Program-2-Ruby-on-Rails/WolfEvents") + ).toBeInTheDocument(); + expect(screen.getByText("README.md")).toBeInTheDocument(); + }); + + test("renders round selector", () => { + setup(); + expect(screen.getByText(/Round/i)).toBeInTheDocument(); + }); + + test("renders word count checkboxes", () => { + setup(); + expect(screen.getByLabelText(/> 10 Word Comments/i)).toBeInTheDocument(); + expect(screen.getByLabelText(/> 20 Word Comments/i)).toBeInTheDocument(); + }); + + test("shows Word Count column when checkbox is selected", async () => { + setup(); + + // Before clicking: Word Count column should not be visible + expect(screen.queryByText(/Word Count/i)).not.toBeInTheDocument(); + + // Click the checkbox to show word count + const checkbox10 = screen.getByLabelText(/> 10 Word Comments/i); + fireEvent.click(checkbox10); + + // Wait for DOM update + await waitFor(() => { + expect(screen.getAllByText(/Word Count/i).length).toBeGreaterThan(0); + }); + }); + + test("toggle sort by total score", () => { + setup(); + const button = screen.getByText(/Sort by Total Review Score/i); + fireEvent.click(button); + expect(button.textContent).toMatch(/desc|asc/i); + }); +}); +