diff --git a/package-lock.json b/package-lock.json index e902b38..2d63c82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,13 +23,15 @@ "@types/react-redux": "^7.1.25", "@types/react-router-dom": "^5.3.3", "axios": "^1.4.0", - "bootstrap": "^5.2.3", + "bootstrap": "^5.3.3", "formik": "^2.2.9", + "jquery": "^3.7.1", "jwt-decode": "^3.1.2", "react": "^18.2.0", "react-bootstrap": "^2.7.4", "react-datepicker": "^4.11.0", "react-dom": "^18.2.0", + "react-i18next": "^14.1.0", "react-icons": "^4.9.0", "react-redux": "^8.0.5", "react-router-dom": "^6.11.1", @@ -42,6 +44,8 @@ "yup": "^1.1.1" }, "devDependencies": { + "@types/jquery": "^3.5.29", + "@types/jqueryui": "^1.12.21", "@types/react-bootstrap": "^0.32.32", "@types/react-datepicker": "^4.10.0", "eslint": "^8.38.0", @@ -2098,9 +2102,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz", + "integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -3962,6 +3966,24 @@ "pretty-format": "^27.0.0" } }, + "node_modules/@types/jquery": { + "version": "3.5.29", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.29.tgz", + "integrity": "sha512-oXQQC9X9MOPRrMhPHHOsXqeQDnWeCDT3PelUIg/Oy8FAbzSZtFHRjc7IpbfFVmpLtJ+UOoywpRsuO5Jxjybyeg==", + "dev": true, + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/jqueryui": { + "version": "1.12.21", + "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.21.tgz", + "integrity": "sha512-hsTOaWPg963smNdoHbEN2anu4vVWj9k2xuaZMIajWERPikaBRG49RmaDA/tb2HldX9/a0qHvQYKipXHSLhM3qA==", + "dev": true, + "dependencies": { + "@types/jquery": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", @@ -4147,6 +4169,12 @@ "@types/node": "*" } }, + "node_modules/@types/sizzle": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.8.tgz", + "integrity": "sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==", + "dev": true + }, "node_modules/@types/sockjs": { "version": "0.3.35", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.35.tgz", @@ -5411,9 +5439,9 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/bootstrap": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", - "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", "funding": [ { "type": "github", @@ -8884,6 +8912,14 @@ "node": ">= 12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-webpack-plugin": { "version": "5.5.3", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz", @@ -9055,6 +9091,29 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "23.10.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.1.tgz", + "integrity": "sha512-NDiIzFbcs3O9PXpfhkjyf7WdqFn5Vq6mhzhtkXzj51aOcNuPNcTwuYNuXCpHsanZGHlHKL35G7huoFeVic1hng==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "peer": true, + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -10888,6 +10947,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/jquery": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13908,6 +13972,27 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "node_modules/react-i18next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.0.tgz", + "integrity": "sha512-3KwX6LHpbvGQ+sBEntjV4sYW3Zovjjl3fpoHbUwSgFHf0uRBcbeCBLR5al6ikncI5+W0EFb71QXZmfop+J6NrQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-icons": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", @@ -16376,6 +16461,14 @@ "node": ">= 0.8" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index c8cb50f..ca465ca 100644 --- a/package.json +++ b/package.json @@ -18,13 +18,15 @@ "@types/react-redux": "^7.1.25", "@types/react-router-dom": "^5.3.3", "axios": "^1.4.0", - "bootstrap": "^5.2.3", + "bootstrap": "^5.3.3", "formik": "^2.2.9", + "jquery": "^3.7.1", "jwt-decode": "^3.1.2", "react": "^18.2.0", "react-bootstrap": "^2.7.4", "react-datepicker": "^4.11.0", "react-dom": "^18.2.0", + "react-i18next": "^14.1.0", "react-icons": "^4.9.0", "react-redux": "^8.0.5", "react-router-dom": "^6.11.1", @@ -61,6 +63,8 @@ ] }, "devDependencies": { + "@types/jquery": "^3.5.29", + "@types/jqueryui": "^1.12.21", "@types/react-bootstrap": "^0.32.32", "@types/react-datepicker": "^4.10.0", "eslint": "^8.38.0", diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 2a68616..0000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React from 'react'; -import { render, screen } from '@testing-library/react'; -import App from './App'; - -test('renders learn react link', () => { - render(); - const linkElement = screen.getByText(/learn react/i); - expect(linkElement).toBeInTheDocument(); -}); diff --git a/src/App.tsx b/src/App.tsx index e3e7ab6..29b5d62 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import React from "react"; -import {createBrowserRouter,Navigate,RouterProvider} from "react-router-dom"; +import { createBrowserRouter, Navigate, RouterProvider } from "react-router-dom"; import AdministratorLayout from "./layout/Administrator"; import ManageUserTypes, { loader as loadUsers } from "./pages/Administrator/ManageUserTypes"; import Login from "./pages/Authentication/Login"; @@ -30,6 +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"; function App() { const router = createBrowserRouter([ @@ -41,7 +42,19 @@ function App() { { index: true, element: } /> }, { path: "login", element: }, { path: "logout", element: } /> }, - { path: "edit-questionnaire", element: } /> }, + // Add the ViewTeamGrades route + { + path: "view-team-grades", + element: + + + } />, + }, + { + path: "edit-questionnaire", + element: } />, + }, { path: "assignments", element: } leastPrivilegeRole={ROLE.TA} />, @@ -106,7 +119,6 @@ function App() { }, ], }, - // Fixed the missing comma and added an opening curly brace { path: "courses", element: } leastPrivilegeRole={ROLE.TA} />, @@ -132,7 +144,7 @@ function App() { }, ] }, - ], // Added the missing closing curly brace + ], }, { path: "administrator", @@ -183,13 +195,13 @@ function App() { path: "new", element: , }, + { path: "edit/:id", element: , }, ], }, - // Add the "Questionnaire" route here { path: "questionnaire", element: , @@ -197,13 +209,12 @@ function App() { ], }, { path: "*", element: }, - // Add the "Questionnaire" route here if it's not under the administrator section - { path: "questionnaire", element: }, + { path: "questionnaire", element: }, // Added the Questionnaire route ], }, ]); - + return ; } -export default App; +export default App; \ No newline at end of file diff --git a/src/layout/Header.tsx b/src/layout/Header.tsx index 8f6295f..8e511a0 100644 --- a/src/layout/Header.tsx +++ b/src/layout/Header.tsx @@ -102,6 +102,9 @@ const Header: React.FC = () => { Student View + + ViewGrades + User: {auth.user.full_name} diff --git a/src/pages/ViewTeamGrades/App.tsx b/src/pages/ViewTeamGrades/App.tsx new file mode 100644 index 0000000..61139fd --- /dev/null +++ b/src/pages/ViewTeamGrades/App.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import ReviewTable from './ReviewTable'; // Importing the ReviewTable component + +// Interface defining the structure of ReviewData +export interface ReviewData { + questionNumber: string; + questionText: string; + reviews: { score: number; comment?: string }[]; // Array of objects with score and optional comment + RowAvg: number; // Average score for the row + maxScore: number; // Maximum possible score +} + +// Functional component App, which renders the ReviewTable +const App: React.FC = () => { + return ( +
+ {/* Rendering the ReviewTable component */} +
+ ); +}; + +export default App; // Exporting the App component as default diff --git a/src/pages/ViewTeamGrades/Data/dummy.json b/src/pages/ViewTeamGrades/Data/dummy.json new file mode 100644 index 0000000..59ade2a --- /dev/null +++ b/src/pages/ViewTeamGrades/Data/dummy.json @@ -0,0 +1,96 @@ +[ + [ + { + "reviewNumber": "1", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 5, "comment": "Great work on this aspect!"}, + {"question": "How user-friendly is this feature?", "score": 2, "comment": "The interface was intuitive and easy to navigate." }, + {"question": "Does this feature meet the project requirements?", "score": 1}, + {"question": "How would you rate the performance of this feature?", "score": 4, "comment": "Performance could be improved, especially for larger datasets." } + ] + }, + { + "reviewNumber": "2", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 5, "comment": "Could use some improvement here." }, + {"question": "How user-friendly is this feature?", "score": 5, "comment": "There were some confusing elements that could be simplified." }, + {"question": "Does this feature meet the project requirements?", "score": 1}, + {"question": "How would you rate the performance of this feature?", "score": 2, "comment": "The feature's performance is generally satisfactory." } + ] + }, + { + "reviewNumber": "3", + "reviews": [ + {"question": "What is the main purpose of this feature?", "comment": "The presentation was well-organized and clear. However, some points could have been elaborated further to provide a deeper understanding of the topic."}, + {"question": "How user-friendly is this feature?", "score": 5, "comment": "The feature was straightforward to use, with clear instructions."}, + {"question": "Does this feature meet the project requirements?", "score": 2}, + {"question": "How would you rate the performance of this feature?", "score": 3, "comment": "Performance issues were encountered during testing." } + ] + }, + { + "reviewNumber": "4", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 3, "comment": "The speaker demonstrated a profound understanding of the subject matter, making the session engaging and informative."}, + {"question": "How user-friendly is this feature?", "score": 2, "comment": "The user experience could be improved, especially for new users." }, + {"question": "Does this feature meet the project requirements?", "score": 0}, + {"question": "How would you rate the performance of this feature?", "score": 1, "comment": "Overall, the feature performs well, but some optimizations could enhance speed." } + ] + }, + { + "reviewNumber": "5", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 1, "comment": "The visuals were compelling and helped in understanding complex concepts easily. However, there were a few slides with too much text, which made it hard to follow at times."}, + {"question": "How user-friendly is this feature?", "score": 4, "comment": "Some aspects were user-friendly, but others required a learning curve." }, + {"question": "Does this feature meet the project requirements?", "score": 2}, + {"question": "How would you rate the performance of this feature?", "score": 4, "comment": "The feature's performance is acceptable but could be faster." } + ] + } + ], + [ + { + "reviewNumber": "1", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 1}, + {"question": "How user-friendly is this feature?", "score": 1, "comment": "Certain design elements make the feature a joy to use." }, + {"question": "Does this feature meet the project requirements?", "score": 3}, + {"question": "How would you rate the performance of this feature?", "score": 4, "comment": "The feature's design is inviting and approachable." } + ] + }, + { + "reviewNumber": "2", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 1, "comment": "Some design choices could enhance user engagement." }, + {"question": "How user-friendly is this feature?", "score": 5, "comment": "The feature's design excels in simplicity and effectiveness."}, + {"question": "Does this feature meet the project requirements?", "score": 2}, + {"question": "How would you rate the performance of this feature?", "score": 3, "comment": "The feature's performance is generally satisfactory." } + ] + }, + { + "reviewNumber": "3", + "reviews": [ + {"question": "What is the main purpose of this feature?", "comment": "The presentation was well-organized and clear. However, some points could have been elaborated further to provide a deeper understanding of the topic."}, + {"question": "How user-friendly is this feature?", "score": 3}, + {"question": "Does this feature meet the project requirements?", "score": 4}, + {"question": "How would you rate the performance of this feature?", "score": 5, "comment": "Performance issues were encountered during testing." } + ] + }, + { + "reviewNumber": "4", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 5, "comment": "The speaker demonstrated a profound understanding of the subject matter, making the session engaging and informative."}, + {"question": "How user-friendly is this feature?", "score": 2}, + {"question": "Does this feature meet the project requirements?", "score": 0}, + {"question": "How would you rate the performance of this feature?", "score": 3, "comment": "Overall, the feature performs well, but some optimizations could enhance speed." } + ] + }, + { + "reviewNumber": "5", + "reviews": [ + {"question": "What is the main purpose of this feature?", "score": 4, "comment": "The visuals were compelling and helped in understanding complex concepts easily. However, there were a few slides with too much text, which made it hard to follow at times."}, + {"question": "How user-friendly is this feature?", "score": 3}, + {"question": "Does this feature meet the project requirements?", "score": 1}, + {"question": "How would you rate the performance of this feature?", "score": 2, "comment": "The feature's performance is acceptable but could be faster." } + ] + } + ] +] \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/Data/dummyData.json b/src/pages/ViewTeamGrades/Data/dummyData.json new file mode 100644 index 0000000..432a1df --- /dev/null +++ b/src/pages/ViewTeamGrades/Data/dummyData.json @@ -0,0 +1,7 @@ +{ + "team": "Straw Hat Pirates", + "members": ["Chaitanya Srusti", "Nisarg Nilesh Doshi", "Aniruddha Rajnekar", "Malick, Kashika"], + "grade": "Grade for submission", + "comment": "Comment for submission", + "late_penalty": 0 +} \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/Data/heatMapData.json b/src/pages/ViewTeamGrades/Data/heatMapData.json new file mode 100644 index 0000000..3a7c4dc --- /dev/null +++ b/src/pages/ViewTeamGrades/Data/heatMapData.json @@ -0,0 +1,371 @@ +[ + [ + { + "questionNumber": "1", + "questionText": "What is the main purpose of this feature?", + "reviews": [ + { "score": 4, "comment": "Great work on this aspect!" }, + { "score": 3, "comment": "Could use some improvement here." }, + { "score": 4, "comment": "The presentation was well-organized and clear. However, some points could have been elaborated further to provide a deeper understanding of the topic." }, + { "score": 5, "comment": "The speaker demonstrated a profound understanding of the subject matter, making the session engaging and informative." }, + { "score": 4, "comment": "The visuals were compelling and helped in understanding complex concepts easily. However, there were a few slides with too much text, which made it hard to follow at times." }, + { "score": 5, "comment": "The use of real-world examples made the concepts more relatable and easier to grasp. Additionally, the speaker was engaging and kept the audience hooked throughout." }, + { "score": 4, "comment": "The interactive exercises were beneficial in reinforcing the learning. However, there were a few technical glitches that disrupted the flow." }, + { "score": 5, "comment": "The hands-on activities were the highlight of the session, providing practical experience that complemented the theoretical learning." }, + { "score": 4, "comment": "The guest speaker brought a fresh perspective to the topic, offering valuable insights that sparked further discussions among the participants." }, + { "score": 5, "comment": "The case studies presented were enlightening, providing practical examples that showcased the application of theoretical concepts." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "2", + "questionText": "How user-friendly is this feature?", + "reviews": [ + { "score": 4, "comment": "The interface was intuitive and easy to navigate." }, + { "score": 2, "comment": "There were some confusing elements that could be simplified." }, + { "score": 5, "comment": "The feature was straightforward to use, with clear instructions." }, + { "score": 2, "comment": "The user experience could be improved, especially for new users." }, + { "score": 3, "comment": "Some aspects were user-friendly, but others required a learning curve." }, + { "score": 2, "comment": "More tooltips or hints could enhance the user-friendliness." }, + { "score": 3, "comment": "Overall, the feature was easy to grasp, but minor improvements could enhance the user experience." }, + { "score": 4, "comment": "The feature was generally user-friendly, with a few areas for improvement." }, + { "score": 3, "comment": "Certain functions were straightforward, while others could use simplification." }, + { "score": 2, "comment": "The feature would benefit from clearer labels and instructions." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "3", + "questionText": "Does this feature meet the project requirements?", + "reviews": [ + { "score": 1}, + { "score": 1}, + { "score": 1}, + { "score": 0}, + { "score": 1}, + { "score": 0}, + { "score": 1}, + { "score": 1}, + { "score": 1}, + { "score": 0} + ], + "RowAvg": 0, + "maxScore": 1 + }, + { + "questionNumber": "4", + "questionText": "How would you rate the performance of this feature?", + "reviews": [ + { "score": 4, "comment": "The feature performs adequately under normal conditions." }, + { "score": 2, "comment": "Performance could be improved, especially for larger datasets." }, + { "score": 4, "comment": "The feature's performance is generally satisfactory." }, + { "score": 1, "comment": "Performance issues were encountered during testing." }, + { "score": 3, "comment": "Overall, the feature performs well, but some optimizations could enhance speed." }, + { "score": 2, "comment": "The feature's performance is acceptable but could be faster." }, + { "score": 3, "comment": "The feature handles most tasks efficiently, but a few functions could be optimized." }, + { "score": 4, "comment": "Performance is stable and meets expectations for regular use." }, + { "score": 3, "comment": "The feature's performance meets the needs for the intended tasks." }, + { "score": 2, "comment": "There were occasional lags in performance during heavy usage." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "5", + "questionText": "What are your thoughts on the design of this feature?", + "reviews": [ + { "score": 4, "comment": "The design is sleek and modern, enhancing usability." }, + { "score": 3, "comment": "Some design elements could be more cohesive." }, + { "score": 5, "comment": "The feature's design is intuitive and visually appealing." }, + { "score": 2, "comment": "The design could be more user-centric." }, + { "score": 4, "comment": "Overall, the design facilitates ease of use." }, + { "score": 3, "comment": "Certain design choices enhance functionality, while others could be refined." }, + { "score": 4, "comment": "The design aligns well with the feature's purpose." }, + { "score": 5, "comment": "The design elements contribute to a seamless user experience." }, + { "score": 4, "comment": "Design considerations are apparent, benefiting user interaction." }, + { "score": 2, "comment": "Some design aspects may confuse new users." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "6", + "questionText": "Were the documentation and help resources helpful?", + "reviews": [ + { "score": 3, "comment": "The design is average, with room for improvement." }, + { "score": 5, "comment": "Certain design elements enhance usability effectively." }, + { "score": 5, "comment": "The design stands out with its intuitive layout." }, + { "score": 3, "comment": "Some design aspects could be more cohesive." }, + { "score": 5, "comment": "The feature's design is modern and visually appealing." }, + { "score": 5, "comment": "Design considerations are apparent, benefiting user interaction." }, + { "score": 3, "comment": "The design offers room for improvement in certain areas." }, + { "score": 4, "comment": "The feature's design is clean and uncluttered." }, + { "score": 5, "comment": "Certain design elements contribute significantly to usability." }, + { "score": 3, "comment": "The design could be more user-centric." } + ], + + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "7", + "questionText": "Did the feature perform well under stress/load?", + "reviews": [ + { "score": 5, "comment": "The design is sleek and modern, enhancing usability." }, + { "score": 5, "comment": "The feature's design is intuitive and visually appealing." }, + { "score": 3, "comment": "Some design elements could be streamlined for clarity." }, + { "score": 5, "comment": "Overall, the design facilitates ease of use." }, + { "score": 5, "comment": "The design elements contribute to a seamless user experience." }, + { "score": 3, "comment": "Certain design choices enhance functionality, while others could be refined." }, + { "score": 5, "comment": "The design aligns well with the feature's purpose." }, + { "score": 5, "comment": "The design delights users with its attention to detail." }, + { "score": 3, "comment": "Some design aspects may confuse new users." }, + { "score": 4, "comment": "The design balances aesthetics with functionality effectively." } + ], + + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "8", + "questionText": "How satisfied are you with the support provided for this feature?", + "reviews": [ + { "score": 3 }, + { "score": 4 }, + { "score": 5 }, + { "score": 2 }, + { "score": 4 }, + { "score": 3 }, + { "score": 4 }, + { "score": 5 }, + { "score": 4 }, + { "score": 2 } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "9", + "questionText": "Would you recommend this feature to others?", + "reviews": [ + { "score": 5, "comment": "The design is exceptional, exceeding expectations." }, + { "score": 3, "comment": "Certain design aspects could be polished further." }, + { "score": 5, "comment": "Overall, the design enhances user experience effectively." }, + { "score": 5, "comment": "The feature's design is top-notch, setting a new standard." }, + { "score": 3, "comment": "Some design elements could be more intuitive." }, + { "score": 5, "comment": "The design offers a pleasant user journey." }, + { "score": 5, "comment": "Design considerations are evident, making tasks straightforward." }, + { "score": 3, "comment": "Certain design choices may require further refinement." }, + { "score": 5, "comment": "The design adapts well to different screen sizes and devices." }, + { "score": 5, "comment": "The feature's design is a joy to interact with." } + ], + + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "10", + "questionText": "Overall, how would you rate this feature?", + "reviews": [ + { "score": 4, "comment": "The design is polished and professional." }, + { "score": 5, "comment": "Certain design elements make the feature a joy to use." }, + { "score": 3, "comment": "Some design aspects could benefit from refinement." }, + { "score": 5, "comment": "The design is user-friendly, with intuitive navigation." }, + { "score": 5, "comment": "Certain design elements enhance user interaction effectively." }, + { "score": 3, "comment": "Some design aspects may require further attention." }, + { "score": 5, "comment": "The design encourages exploration and discovery." }, + { "score": 5, "comment": "The feature's design sets a new standard for user interfaces." }, + { "score": 3, "comment": "Certain design elements are confusing and could be clarified." }, + { "score": 5, "comment": "The design offers an inviting and engaging experience." } + ], + + "RowAvg": 0, + "maxScore": 5 + } + ], + [ + { + "questionNumber": "1", + "questionText": "What is the main purpose of this feature?", + "reviews": [ + { "score": 4, "comment": "The design is polished and professional." }, + { "score": 5, "comment": "Certain design elements make the feature a joy to use." }, + { "score": 3, "comment": "Some design aspects could benefit from refinement." }, + { "score": 4, "comment": "The design is user-friendly, with intuitive navigation." }, + { "score": 5, "comment": "Certain design elements enhance user interaction effectively." }, + { "score": 4, "comment": "Some design aspects may require further attention." }, + { "score": 4, "comment": "The design encourages exploration and discovery." }, + { "score": 5, "comment": "The feature's design sets a new standard for user interfaces." }, + { "score": 4, "comment": "Certain design elements are confusing and could be clarified." }, + { "score": 4, "comment": "The design offers an inviting and engaging experience." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "2", + "questionText": "How user-friendly is this feature?", + "reviews": [ + { "score": 5, "comment": "The design is flawless, making tasks effortless." }, + { "score": 2, "comment": "Some design aspects could be more user-oriented." }, + { "score": 4, "comment": "The feature's design is inviting and approachable." }, + { "score": 5, "comment": "Certain design elements provide a delightful user journey." }, + { "score": 2, "comment": "Some design choices could enhance user engagement." }, + { "score": 4, "comment": "The design adapts well to varying user needs." }, + { "score": 5, "comment": "The feature's design excels in simplicity and effectiveness." }, + { "score": 3, "comment": "Certain design elements could benefit from more visual hierarchy." }, + { "score": 4, "comment": "The design offers a pleasing aesthetic while being functional." }, + { "score": 5, "comment": "The feature's design is intuitive and user-centric." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "3", + "questionText": "Does this feature meet the project requirements?", + "reviews": [ + { "score": 1}, + { "score": 1}, + { "score": 1}, + { "score": 0}, + { "score": 1}, + { "score": 0}, + { "score": 1}, + { "score": 0}, + { "score": 0}, + { "score": 1 } + ], + "RowAvg": 0, + "maxScore": 1 + }, + { + "questionNumber": "4", + "questionText": "How would you rate the performance of this feature?", + "reviews": [ + { "score": 5, "comment": "The design is exceptional, exceeding expectations." }, + { "score": 5, "comment": "Certain design aspects could be polished further." }, + { "score": 4, "comment": "Overall, the design enhances user experience effectively." }, + { "score": 5, "comment": "The feature's design is top-notch, setting a new standard." }, + { "score": 0, "comment": "Some design elements could be more intuitive." }, + { "score": 4, "comment": "The design offers a pleasant user journey." }, + { "score": 5, "comment": "Design considerations are evident, making tasks straightforward." }, + { "score": 5, "comment": "Certain design choices may require further refinement." }, + { "score": 5, "comment": "The design adapts well to different screen sizes and devices." }, + { "score": 5, "comment": "The feature's design is a joy to interact with." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "5", + "questionText": "What are your thoughts on the design of this feature?", + "reviews": [ + { "score": 4, "comment": "The design is polished and professional." }, + { "score": 5, "comment": "Certain design elements make the feature a joy to use." }, + { "score": 3, "comment": "Some design aspects could benefit from refinement." }, + { "score": 4, "comment": "The design is user-friendly, with intuitive navigation." }, + { "score": 5, "comment": "Certain design elements enhance user interaction effectively." }, + { "score": 2, "comment": "Some design aspects may require further attention." }, + { "score": 4, "comment": "The design encourages exploration and discovery." }, + { "score": 5, "comment": "The feature's design sets a new standard for user interfaces." }, + { "score": 4, "comment": "Certain design elements are confusing and could be clarified." }, + { "score": 4, "comment": "The design offers an inviting and engaging experience." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "6", + "questionText": "Were the documentation and help resources helpful?", + "reviews": [ + { "score": 5, "comment": "The design is flawless, making tasks effortless." }, + { "score": 3, "comment": "Some design aspects could be more user-oriented." }, + { "score": 5, "comment": "The feature's design is inviting and approachable." }, + { "score": 5, "comment": "Certain design elements provide a delightful user journey." }, + { "score": 3, "comment": "Some design choices could enhance user engagement." }, + { "score": 4, "comment": "The design adapts well to varying user needs." }, + { "score": 5, "comment": "The feature's design excels in simplicity and effectiveness." }, + { "score": 1, "comment": "Certain design elements could benefit from more visual hierarchy." }, + { "score": 5, "comment": "The design offers a pleasing aesthetic while being functional." }, + { "score": 5, "comment": "The feature's design is intuitive and user-centric." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "7", + "questionText": "Did the feature perform well under stress/load?", + "reviews": [ + { "score": 4, "comment": "The design is sleek and modern, enhancing usability." }, + { "score": 5, "comment": "The feature's design is intuitive and visually appealing." }, + { "score": 3, "comment": "Some design elements could be streamlined for clarity." }, + { "score": 4, "comment": "Overall, the design facilitates ease of use." }, + { "score": 5, "comment": "The design elements contribute to a seamless user experience." }, + { "score": 2, "comment": "Certain design choices enhance functionality, while others could be refined." }, + { "score": 4, "comment": "The design aligns well with the feature's purpose." }, + { "score": 5, "comment": "The design delights users with its attention to detail." }, + { "score": 2, "comment": "Some design aspects may confuse new users." }, + { "score": 4, "comment": "The design balances aesthetics with functionality effectively." } + ], + + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "8", + "questionText": "How satisfied are you with the support provided for this feature?", + "reviews": [ + { "score": 5, "comment": "The design is exceptional, exceeding expectations." }, + { "score": 4, "comment": "Certain design aspects could be polished further." }, + { "score": 4, "comment": "Overall, the design enhances user experience effectively." }, + { "score": 5, "comment": "The feature's design is top-notch, setting a new standard." }, + { "score": 2, "comment": "Some design elements could be more intuitive." }, + { "score": 4, "comment": "The design offers a pleasant user journey." }, + { "score": 5, "comment": "Design considerations are evident, making tasks straightforward." }, + { "score": 3, "comment": "Certain design choices may require further refinement." }, + { "score": 4, "comment": "The design adapts well to different screen sizes and devices." }, + { "score": 5, "comment": "The feature's design is a joy to interact with." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "9", + "questionText": "Would you recommend this feature to others?", + "reviews": [ + { "score": 5, "comment": "The design is flawless, making tasks effortless." }, + { "score": 3, "comment": "Some design aspects could be more user-oriented." }, + { "score": 4, "comment": "The feature's design is inviting and approachable." }, + { "score": 5, "comment": "Certain design elements provide a delightful user journey." }, + { "score": 2, "comment": "Some design choices could enhance user engagement." }, + { "score": 4, "comment": "The design adapts well to varying user needs." }, + { "score": 5, "comment": "The feature's design excels in simplicity and effectiveness." }, + { "score": 2, "comment": "Certain design elements could benefit from more visual hierarchy." }, + { "score": 4, "comment": "The design offers a pleasing aesthetic while being functional." }, + { "score": 5, "comment": "The feature's design is intuitive and user-centric." } + ], + "RowAvg": 0, + "maxScore": 5 + }, + { + "questionNumber": "10", + "questionText": "Overall, how would you rate this feature?", + "reviews": [ + { "score": 4, "comment": "The design is sleek and modern, enhancing usability." }, + { "score": 5, "comment": "The feature's design is intuitive and visually appealing." }, + { "score": 3, "comment": "Some design elements could be streamlined for clarity." }, + { "score": 4, "comment": "Overall, the design facilitates ease of use." }, + { "score": 3, "comment": "The design elements contribute to a seamless user experience." }, + { "score": 3, "comment": "Certain design choices enhance functionality, while others could be refined. Overall scope of improvement" }, + { "score": 4, "comment": "The design aligns well with the feature's purpose." }, + { "score": 5, "comment": "The design delights users with its attention to detail." }, + { "score": 4, "comment": "Some design aspects may confuse new users." }, + { "score": 4, "comment": "The design balances aesthetics with functionality effectively." } + ], + "RowAvg": 0, + "maxScore": 5 + } + ] +] \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/ReviewTable.test.tsx b/src/pages/ViewTeamGrades/ReviewTable.test.tsx new file mode 100644 index 0000000..5b95c1a --- /dev/null +++ b/src/pages/ViewTeamGrades/ReviewTable.test.tsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import { BrowserRouter as Router } from 'react-router-dom'; +import ReviewTable from './ReviewTable'; + +describe('ReviewTable component', () => { + test('renders without crashing', () => { + render( + {/* Wrap your component with Router */} + + + ); + }); + + + // Add more tests as needed +}); diff --git a/src/pages/ViewTeamGrades/ReviewTable.tsx b/src/pages/ViewTeamGrades/ReviewTable.tsx new file mode 100644 index 0000000..092194b --- /dev/null +++ b/src/pages/ViewTeamGrades/ReviewTable.tsx @@ -0,0 +1,191 @@ +import React, { useState } from 'react'; +import ReviewTableRow from './ReviewTableRow'; +import RoundSelector from './RoundSelector'; +import dummyDataRounds from './Data/heatMapData.json'; // Ensure this import aligns with actual data structure +import dummyData from './Data/dummyData.json'; +import { calculateAverages, getColorClass } from './utils'; +import './grades.scss'; +import { Link } from 'react-router-dom'; +import ShowSubmission from './ShowSubmission'; +import { ReviewData } from './App'; // Adjust the import path as necessary + +const ReviewTable: React.FC = () => { + const [currentRound, setCurrentRound] = useState(0); + const [sortOrderRow, setSortOrderRow] = useState<'asc' | 'desc' | 'none'>('none'); + const [showWordCount10, setShowWordCount10] = useState(false); + const [showWordCount20, setShowWordCount20] = useState(false); + const [showFullQuestion, setShowFullQuestion] = useState(false); + const [showAll, setShowAll] = useState(false); + + const toggleSortOrderRow = () => { + setSortOrderRow((prevSortOrder) => { + if (prevSortOrder === 'asc') return 'desc'; + if (prevSortOrder === 'desc') return 'none'; + return 'asc'; + }); + }; + + // const currentRoundData = dummyDataRounds[currentRound]; + const { averagePeerReviewScore } = showAll + ? calculateAverages(dummyDataRounds, 'none') + : calculateAverages([dummyDataRounds[currentRound]], 'none'); + + const handleRoundChange = (roundIndex: number) => { + setCurrentRound(roundIndex); + setShowAll(false); + }; + + const showAllRounds = () => { + setShowAll(true); + }; + + const colorLegend = [ + { color: 'Red', description: 'Poor', className: 'c1' }, + { color: 'Orange', description: 'Fair', className: 'c2' }, + { color: 'Yellow', description: 'Average', className: 'c3' }, + { color: 'LightGreen', description: 'Good', className: 'c4' }, + { color: 'DarkGreen', description: 'Excellent', className: 'c5' }, + ]; + + const renderTableForRound = (roundData: ReviewData[], roundIndex: number) => { + const { columnAverages, sortedData } = calculateAverages([roundData], sortOrderRow); + + return ( + <> +

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

+
+ + + + + {Array.from({ length: roundData[0].reviews.length }, (_, i) => ( + + ))} + + {showWordCount10 && } + {showWordCount20 && } + + + + {sortedData.map((row, index) => ( + + ))} + + + {columnAverages.map((avg, index) => ( + + ))} + + +
Question No.{`Review ${i + 1}`} + Avg + {sortOrderRow === "none" && ▲▼} + {sortOrderRow === "asc" && } + {sortOrderRow === "desc" && } + 10+ Words20+ Words
Avg + {avg.toFixed(2)} +
+

+
+ + ); + }; + + return ( +
+
+
+ +   Color Legend   + +
+ + + + + + + + + {colorLegend.map((item) => ( + + + + + ))} + +
ColorRating
{item.description}
+
+
+
+

Summary Report for assignment: Program 2

+
Team: {dummyData.team}
+
+ Average peer review score:{" "} + {averagePeerReviewScore} +
+ +

+

+
+ setShowWordCount10(e.target.checked)} + /> + + setShowWordCount20(e.target.checked)} + /> + +          + setShowFullQuestion(e.target.checked)} + /> + +
+

+ + + {showAll ? ( + <> + {dummyDataRounds.map((roundData, index) => ( +
+ {renderTableForRound(roundData, index)} +
+ ))} + + ) : ( +
+ {renderTableForRound(dummyDataRounds[currentRound], currentRound)} +
+ )} + +

+

Grade and comment for submission

+ Grade: {dummyData.grade}

+ Comment: {dummyData.comment}

+ Late Penalty: {dummyData.late_penalty}

+

+ Back +
+ ); +}; + +export default ReviewTable; \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/ReviewTableRow.test.tsx b/src/pages/ViewTeamGrades/ReviewTableRow.test.tsx new file mode 100644 index 0000000..b046554 --- /dev/null +++ b/src/pages/ViewTeamGrades/ReviewTableRow.test.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // Import jest-dom for custom assertions +import ReviewTableRow from './ReviewTableRow'; // Import the component to test + +// Mocked ReviewData +const mockReviewData = { + questionText: 'How do you like the product?', + questionNumber: 'Q1', + maxScore: 5, + RowAvg: 4.2, + reviews: [ + { score: 5, comment: 'Great product!' }, + { score: 4, comment: 'Good product.' }, + { score: 3, comment: 'Average product.' } + ] +}; + +describe('ReviewTableRow', () => { + test('renders question number by default', () => { + render(); + expect(screen.getByText('Q1')).toBeInTheDocument(); + }); + + test('renders full question text when showFullQuestion is true', () => { + render(); + expect(screen.getByText('How do you like the product?')).toBeInTheDocument(); + }); + + test('renders reviews with correct scores and comments', () => { + render(); + + // Find the score within the circle span + const circleScoreElement = screen.getByRole('cell', { name: '5' }); + + // Find the scores within the underlined spans + const underlinedScoreElements = screen.getAllByRole('cell', { name: /5/ }); + + // Assert that the elements are present + expect(circleScoreElement).toBeInTheDocument(); + expect(underlinedScoreElements.length).toBe(2); // Assuming all scores are underlined + // Add assertions for comments if needed + }); +}); diff --git a/src/pages/ViewTeamGrades/ReviewTableRow.tsx b/src/pages/ViewTeamGrades/ReviewTableRow.tsx new file mode 100644 index 0000000..e85be4a --- /dev/null +++ b/src/pages/ViewTeamGrades/ReviewTableRow.tsx @@ -0,0 +1,54 @@ +import React from 'react'; +import { getColorClass, getWordCount10, getWordCount20 } from './utils'; // Importing utility functions +import { ReviewData } from './App'; // Importing the ReviewData interface from App + +// Props interface for ReviewTableRow component +interface ReviewTableRowProps { + row: ReviewData; // Data for the row + showWordCount10: boolean; // Flag to show reviews with 10+ words + showWordCount20: boolean; // Flag to show reviews with 20+ words + showFullQuestion: boolean; // New prop to toggle between question number and full question text + +} + +// Functional component ReviewTableRow +const ReviewTableRow: React.FC = ({ row, showWordCount10, showWordCount20, showFullQuestion }) => { + return ( + + {/* Question Number */} + +
+ {!showFullQuestion && ( // Only show circle or tick when not showing full questions + row.maxScore !== 1 ? ( + {row.maxScore} + ) : ( + + ) + )} +        + {showFullQuestion ? row.questionText : row.questionNumber} +
+ + + {/* Review Cells */} + {row.reviews.map((review, idx) => ( + + {review.score} + + ))} + + {/* Row Average */} + {row.RowAvg.toFixed(2)} + + {/* Optional columns for word count */} + {showWordCount10 && {getWordCount10(row)}} + {showWordCount20 && {getWordCount20(row)}} + + ); +}; + +export default ReviewTableRow; // Exporting the ReviewTableRow component as default diff --git a/src/pages/ViewTeamGrades/RoundSelector.test.tsx b/src/pages/ViewTeamGrades/RoundSelector.test.tsx new file mode 100644 index 0000000..caf16ef --- /dev/null +++ b/src/pages/ViewTeamGrades/RoundSelector.test.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import RoundSelector from './RoundSelector'; +import dummyDataRounds from './Data/heatMapData.json'; + +describe('RoundSelector', () => { + const handleRoundChangeMock = jest.fn(); + const showAllRoundsMock = jest.fn(); + + beforeEach(() => { + render(); + }); + + test('renders without crashing', () => { + expect(screen.getByText(/Team members:/)).toBeInTheDocument(); + }); + + test('renders the correct number of round buttons', () => { + expect(screen.getAllByRole('button')).toHaveLength(dummyDataRounds.length + 1); // +1 for the 'Show All' button + }); + + test('calls handleRoundChange with correct index when a round button is clicked', () => { + const roundButtons = screen.getAllByRole('button'); + fireEvent.click(roundButtons[1]); // Click on "Round 2" + expect(handleRoundChangeMock).toHaveBeenCalledWith(1); + }); + + test('toggles current class correctly when a round button is clicked', () => { + const firstButton = screen.getAllByRole('button')[0]; + fireEvent.click(firstButton); + expect(firstButton).toHaveClass('current'); + const secondButton = screen.getAllByRole('button')[1]; + fireEvent.click(secondButton); + expect(secondButton).toHaveClass('current'); + expect(firstButton).not.toHaveClass('current'); + }); + + test('calls showAllRounds and toggles current class when "Show All" button is clicked', () => { + const showAllButton = screen.getByText('Show All'); + fireEvent.click(showAllButton); + expect(showAllRoundsMock).toHaveBeenCalled(); + expect(showAllButton).toHaveClass('current'); + }); +}); diff --git a/src/pages/ViewTeamGrades/RoundSelector.tsx b/src/pages/ViewTeamGrades/RoundSelector.tsx new file mode 100644 index 0000000..de0ed89 --- /dev/null +++ b/src/pages/ViewTeamGrades/RoundSelector.tsx @@ -0,0 +1,67 @@ +import React, { useState, useEffect } from 'react'; +import dummyDataRounds from './Data/heatMapData.json'; +import teamData from './Data/dummyData.json'; + +interface RoundSelectorProps { + currentRound: number; + handleRoundChange: (roundIndex: number) => void; + showAllRounds: () => void; // Added for showing all rounds +} + +const RoundSelector: React.FC = ({ currentRound, handleRoundChange, showAllRounds }) => { + const [teamMembers, setTeamMembers] = useState([]); + + useEffect(() => { + setTeamMembers(teamData.members); + }, []); + + const active = (clickedButton: HTMLElement) => { + // Remove 'current' class from all buttons + const buttons = document.querySelectorAll('.round-button'); + buttons.forEach(btn => { + btn.classList.remove('current'); + }); + + // Check if 'current' class is already applied to any button + const currentButton = document.querySelector('.round-button.current'); + + // If 'current' class is not applied to any button, add it to the clicked button + if (!currentButton) { + clickedButton.classList.add('current'); + } else { + // If 'current' class is applied to a button, remove it from that button and add it to the clicked button + currentButton.classList.remove('current'); + clickedButton.classList.add('current'); + } + }; + + + return ( +
+
+ {dummyDataRounds.map((round, index) => ( + + ))} + + + Team members: {teamMembers.map((member, index) => ( + + ({member}) + {index !== teamMembers.length - 1 && ' '} + + ))} + +
+
+ ); +}; + +export default RoundSelector; \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/ShowSubmission.test.tsx b/src/pages/ViewTeamGrades/ShowSubmission.test.tsx new file mode 100644 index 0000000..d35d9e5 --- /dev/null +++ b/src/pages/ViewTeamGrades/ShowSubmission.test.tsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { render, screen, fireEvent } from '@testing-library/react'; +import '@testing-library/jest-dom/extend-expect'; // Import jest-dom for custom assertions +import ShowSubmission from './ShowSubmission'; // Import the component to test + +describe('ShowSubmission', () => { + test('renders button with correct text', () => { + render(); + expect(screen.getByRole('button', { name: 'Show Submission' })).toBeInTheDocument(); + }); + + test('collapsible content is initially hidden', () => { + render(); + const collapsibleContent = screen.queryByTestId('example-collapse-text'); // Update test ID here + expect(collapsibleContent).toBeNull(); // Check if the collapsible content is initially null + }); + + + test('renders links when collapsible content is open', () => { + render(); + const button = screen.getByRole('button', { name: 'Show Submission' }); + fireEvent.click(button); + expect(screen.getByText('https://github.ncsu.edu/Program-2-Ruby-on-Rails/WolfEvents')).toBeInTheDocument(); + expect(screen.getByText('http://152.7.177.44:8080/')).toBeInTheDocument(); + }); +}); diff --git a/src/pages/ViewTeamGrades/ShowSubmission.tsx b/src/pages/ViewTeamGrades/ShowSubmission.tsx new file mode 100644 index 0000000..2906ae7 --- /dev/null +++ b/src/pages/ViewTeamGrades/ShowSubmission.tsx @@ -0,0 +1,50 @@ +import React, { useState } from 'react'; +import { Button, Collapse } from 'react-bootstrap'; + +// Component to show submission links with collapsible functionality +const ShowSubmission = () => { + const [open, setOpen] = useState(false); // State to manage collapsible content + + return ( + <> + {/* Button to toggle the collapsible content */} + + + {/* Collapsible content */} + +
+

+ + {/* Render links only when open is true */} + {open && ( + <> + + https://github.ncsu.edu/Program-2-Ruby-on-Rails/WolfEvents + +
+ + http://152.7.177.44:8080/ + + + )} +
+
+ + ); +}; + +export default ShowSubmission; diff --git a/src/pages/ViewTeamGrades/grades.scss b/src/pages/ViewTeamGrades/grades.scss new file mode 100644 index 0000000..c385cf2 --- /dev/null +++ b/src/pages/ViewTeamGrades/grades.scss @@ -0,0 +1,298 @@ +/* Set the maximum width of the table container */ +.table-container { + max-width: 70%; +} + +/* Circle around max score number */ +.circle-container { + display: flex; + align-items: center; +} + +.circle { + width: 15px; + height: 15px; + border-radius: 50%; + border: 1px solid orangered; + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + color: orangered; + margin-left: -20px; /* Add margin to separate the circle from the text */ +} + +.tick{ + display: flex; + justify-content: center; + align-items: center; + font-weight: bold; + color: orangered; + margin-left: -15px; /* Add margin to separate the circle from the text */ +} + +/* Colors used for coloring score cells within the heatgrid for the grades view */ + +/* Null space in the table */ +.c0 { + background-color: #d3d3d3; +} + +/* Red, indicative of a poor score */ +.c1 { + background-color: #ff8080; +} + +/* Orange */ +.c2 { + background-color: #FD992D; +} + +/* Yellow, indicative of a median score */ +.c3 { + background-color: #FFEC8B; +} + +/* Light green */ +.c4 { + background-color: #BCED91; +} + +/* Green, indicative of a good score */ +.c5 { + background-color: #2DE636; +} + +/* Default background color */ +.cf { + background-color: #FFFFFF; +} + +/* Style for the grades in the summary report */ +.grade-circle { + width: 30px; + height: 30px; + border-radius: 50%; + font-size: 15px; + color: black; + line-height: 30px; + text-align: center; +} + +/* Underline scores which have a comment */ +.underlined { + text-decoration: underline; + font-weight: bold; +} + +/* Styling for the heatgrid table */ +.tbl_heat { + border: 1px solid black; + width: 100%; + font-size: 10px; + text-align: center; + table-layout: auto; +} + +.tbl_heat td { + cursor: pointer; + padding: 8px; + border: 1px black solid; + width: auto; + font-size: 11px; + table-layout: fixed; + position: relative; +} + +/* Tooltip display for question text on hover */ +.tbl_heat td[data-question]:hover::after { + content: attr(data-question); + position: absolute; + background-color: rgba($color: #000000, $alpha: 1); + color: #ffffff; + padding: 4px; + border-radius: 4px; + bottom: 130%; + left: 0%; + white-space: nowrap; +} + +/* Styling for table headers */ +.tbl_heat th { + border: 1px black solid; + padding: 10px; + font-size: 11px; + background-color: #f2f2f2; + width: auto; + table-layout: fixed; +} + +/* Hides padding for specific rows */ +.hiddenRow { + padding: 0 !important; +} + +/* Tooltip span styling */ +.spn_tooltip { + padding-left: 30px; + color: grey; + font-size: small; +} + +/* Styling for question toggle span */ +.spn_qsttog { + padding-left: 30px; + cursor: pointer; + text-decoration: underline; + color: blue; + font-size: small; +} + +/* Classes for E2100 Tag Reports for Students to style the new HeatGrid of review tags */ + +/* Styling for action row */ +.action_row { + border: 1px black solid; + padding: 1px 2px 2px 1px; + font-size: 11px; + text-align: center; +} + +/* Styling for tag heat grid */ +.tag_heat_grid { + padding: 0; + spacing: 0; + border: 1px solid black; + position: relative; + float: right; + top: 0px; + right: 0px; + z-index: 2; +} + +/* Styling for tag heat grid headers */ +.tag_heat_grid th { + border: 1px solid black; + font-size: 12px; + cursor: pointer; +} + +/* Styling for tag heat grid cells */ +.tag_heat_grid td { + border: 0.5px solid black; + font-size: 8px; +} + +/* Styling for tag heat grid criterion */ +.tag_heat_grid_criterion { + font-size: 11px !important; + font-weight: bold !important; +} + +/* Styles for the round selector */ +.round-selector { + display: flex; +} + +.round-button { + padding: 7px 7px; + margin: 5px; + border: 2px solid #4a90e2; + border-radius: 10px; + background-color: #4a90e2; + color: white; + font-size: 10px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + + &:hover { + background-color: #357bd8; + border-color: #357bd8; + } + + &.current { + background-color: #2ecc71; + border-color: #2ecc71; + } + + .round-selector > div { + display: flex; + align-items: center; + margin: auto; + } +} + + +.tooltip-text { + visibility: hidden; + width: 280px; + background-color: black; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 0; + + /* Position the tooltip text */ + position: absolute; + z-index: 1; + bottom: 100%; + left: 150%; + margin-left: -140px; /* Use half of the width (280px/2), to center the tooltip */ + + /* Fade in tooltip */ + opacity: 0; + transition: opacity 0.3s; +} + +.question-cell { + text-align: left; // Align text to the left of the cell + padding-left: 10px; // Add padding on the left for better spacing + word-wrap: break-word; // Ensures long words do not overflow + overflow-wrap: break-word; + white-space: normal; // Allows text to wrap normally +} + + +.review-container { + position: relative; + display: flex; + justify-content: space-between; // Adjust as necessary to fit your design + flex-direction: column; // Align children horizontally + width: 100%; // Ensure the container takes full width + align-items: flex-start; + + .table-container { + order: 2; + flex: 1 0 70%; // Your existing max-width but as flex basis + } + + .color-legend-container { + position: absolute; + order: 1; + flex: 0 1 auto; + align-self: flex-end; // Align the legend container to the start of the flex container (top right) + width: auto; // Set the width as required by the content + text-align: center; // Center-align the text inside the legend container + + .color-legend { + margin-bottom: 5px; // Space between the label and the table + font-size: 16px; // Optional: Adjust font size for visibility + } + + table { + border-collapse: collapse; + width: 100%; + + th, td { + border: 1px solid black; + padding: 8px; + text-align: left; + } + + td { + color: #000; // Ensure text is readable, adjust as needed + } + } + } +} \ No newline at end of file diff --git a/src/pages/ViewTeamGrades/utils.ts b/src/pages/ViewTeamGrades/utils.ts new file mode 100644 index 0000000..846285d --- /dev/null +++ b/src/pages/ViewTeamGrades/utils.ts @@ -0,0 +1,79 @@ +import { ReviewData } from './App'; + +// Function to get color class based on score and maxScore +export const getColorClass = (score: number, maxScore: number) => { + let scoreColor = score; + + scoreColor = ((maxScore - scoreColor) / maxScore) * 100; + if (scoreColor >= 80) return 'c1'; + else if (scoreColor >= 60 && scoreColor < 80) return 'c2'; + else if (scoreColor >= 40 && scoreColor < 60) return 'c3'; + else if (scoreColor >= 20 && scoreColor < 40) return 'c4'; + else if (scoreColor >= 0 && scoreColor < 20) return 'c5'; + else return 'cf'; +}; + +// Function to get count of reviews with more than 10 words +export const getWordCount10 = (row: ReviewData) => { + return row.reviews.filter( + (review) => review.comment && review.comment.trim().split(' ').length > 10 + ).length; +}; + +// Function to get count of reviews with more than 20 words +export const getWordCount20 = (row: ReviewData) => { + return row.reviews.filter( + (review) => review.comment && review.comment.trim().split(' ').length > 20 + ).length; +}; + + +// Function to calculate averages for rows and columns, handling multiple rounds +export const calculateAverages = ( + roundsData: ReviewData[][], + sortOrderRow: 'asc' | 'desc' | 'none' +): { averagePeerReviewScore: string; columnAverages: number[]; sortedData: ReviewData[] } => { + let totalAvg = 0; + let totalMaxScore = 0; + let totalQuestions = 0; + + roundsData.forEach((currentRoundData) => { + currentRoundData.forEach((row) => { + const sum = row.reviews.reduce((acc, val) => acc + val.score, 0); + row.RowAvg = sum / row.reviews.length; + totalAvg += row.RowAvg; + totalMaxScore += row.maxScore; + totalQuestions++; + }); + }); + + const averagePeerReviewScore = totalQuestions > 0 + ? (((totalAvg / totalMaxScore) * 100).toFixed(2)) + : '0.00'; + + let columnAverages: number[] = []; + + if (roundsData.length > 0 && roundsData[0].length > 0) { + columnAverages = Array.from({ length: roundsData[0][0].reviews.length }, () => 0); + roundsData.forEach(currentRoundData => { + currentRoundData.forEach(row => { + row.reviews.forEach((val, index) => { + columnAverages[index] += val.score; + }); + }); + }); + + columnAverages = columnAverages.map(sum => parseFloat((sum / totalQuestions).toFixed(2))); + } + + // Sorting data if necessary + let sortedData = roundsData.flat(); + + if (sortOrderRow === 'asc') { + sortedData.sort((a, b) => a.RowAvg - b.RowAvg); + } else if (sortOrderRow === 'desc') { + sortedData.sort((a, b) => b.RowAvg - a.RowAvg); + } + + return { averagePeerReviewScore, columnAverages, sortedData }; +}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index ec5b6f5..885fce5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "lib": [ "dom", "dom.iterable", - "esnext" + "esnext", ], "baseUrl": "src", "allowJs": true,