diff --git a/package-lock.json b/package-lock.json index 37ca0e837..602cbf07a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -92,6 +92,7 @@ "url-polyfill": "^1.1.12", "vite": "^5.4.7", "vite-tsconfig-paths": "^5.0.1", + "xlsx": "^0.18.5", "zod": "^3.23.8" }, "devDependencies": { @@ -23358,6 +23359,15 @@ "node": ">=0.8" } }, + "node_modules/word": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz", + "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -23535,6 +23545,57 @@ } } }, + "node_modules/xlsx": { + "version": "0.18.5", + "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz", + "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", + "license": "Apache-2.0", + "dependencies": { + "adler-32": "~1.3.0", + "cfb": "~1.2.1", + "codepage": "~1.15.0", + "crc-32": "~1.2.1", + "ssf": "~0.11.2", + "wmf": "~1.0.1", + "word": "~0.3.0" + }, + "bin": { + "xlsx": "bin/xlsx.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx/node_modules/adler-32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz", + "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx/node_modules/codepage": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz", + "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/xlsx/node_modules/ssf": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz", + "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", + "license": "Apache-2.0", + "dependencies": { + "frac": "~1.1.2" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/xml-name-validator": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", diff --git a/package.json b/package.json index 827299bc2..3a77adad2 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "url-polyfill": "^1.1.12", "vite": "^5.4.7", "vite-tsconfig-paths": "^5.0.1", + "xlsx": "^0.18.5", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/Mutations/Ratings.tsx b/src/Mutations/Ratings.tsx index 802bbd59e..a2f7bd36a 100644 --- a/src/Mutations/Ratings.tsx +++ b/src/Mutations/Ratings.tsx @@ -149,3 +149,55 @@ export const REJECT_RATING = gql` rejectRating(user: $user, sprint: $sprint) } `; + +export const GET_RATINGS_BY_COHORT = gql` + query getRatingsByCohort($cohortId: String!, $orgToken: String!){ + getRatingsByCohort(cohortId: $cohortId, orgToken: $orgToken){ + id + sprint + } +} +` + +export const ADD_RATINGS_BY_FILE = gql` + mutation addRatingsByFile($file: Upload!, $cohortId: String!, $sprint: Int!, $orgToken: String!){ + addRatingsByFile(file: $file, cohortId: $cohortId, sprint: $sprint orgToken: $orgToken){ + NewRatings { + user { + email + } + sprint + phase + quality + quantity + professional_Skills + feedbacks { + sender { + email + } + content + createdAt + } + cohort { + name + } + } + RejectedRatings{ + email + quantity + quality + professional_skills + feedBacks + } + UpdatedRatings { + quantity + quality + professional_Skills + feedbacks { + content + } + oldFeedback + } + } + } +` diff --git a/src/Mutations/cohortMutations.tsx b/src/Mutations/cohortMutations.tsx index aadad6d5a..1b0c2c9f4 100644 --- a/src/Mutations/cohortMutations.tsx +++ b/src/Mutations/cohortMutations.tsx @@ -43,3 +43,12 @@ export const DeleteCohort = gql` } } `; + +export const GET_USER_COHORTS = gql` +query getUserCohorts($orgToken: String!){ + getUserCohorts(orgToken: $orgToken) { + id + name + } +} +` diff --git a/src/Mutations/teamMutation.tsx b/src/Mutations/teamMutation.tsx index db368f2ff..93927c3ac 100644 --- a/src/Mutations/teamMutation.tsx +++ b/src/Mutations/teamMutation.tsx @@ -53,3 +53,15 @@ export const DeleteTeam = gql` } `; +export const GET_TEAMS_BY_COHORT = gql` + query getTeamsByCohort($cohortId: String!,$orgToken: String!) { + getTeamsByCohort(cohortId: $cohortId,orgToken: $orgToken){ + id + name + members { + email + role + } + } + } +` diff --git a/src/components/BulkRatingModal.tsx b/src/components/BulkRatingModal.tsx new file mode 100644 index 000000000..62059ccda --- /dev/null +++ b/src/components/BulkRatingModal.tsx @@ -0,0 +1,294 @@ +import { useLazyQuery, useMutation } from "@apollo/client" +import React, { useEffect, useRef, useState } from "react" +import { useTranslation } from "react-i18next" +import * as XLSX from "xlsx" +import { ADD_RATINGS_BY_FILE, GET_RATINGS_BY_COHORT } from "../Mutations/Ratings" +import { toast } from "react-toastify" +import { GET_TEAMS_BY_COHORT } from "../Mutations/teamMutation" +import { GET_USER_COHORTS } from "../Mutations/cohortMutations" + +type BulkRatingModalProps = { + bulkRateModal: boolean, + setBulkRateModal: React.Dispatch> +} + +type AddRatingsByFileFormData = { + cohortId: string, + sprint: string, + file: File | null, +} + +const BulkRatingModal = ({ bulkRateModal, setBulkRateModal }: BulkRatingModalProps) => { + const { t } = useTranslation() + const [formData, setFormData] = useState({ + cohortId: '', + sprint: '', + file: null + }) + + const [getUserCohorts, { data: cohorts, loading: loadingCohorts, error: cohortsError }] = useLazyQuery(GET_USER_COHORTS,{ + variables: { + orgToken: localStorage.getItem('orgToken') + }, + fetchPolicy: 'network-only' + }) + const [getRatingsByCohort, { data: ratings, loading: loadingRatings, error: ratingsError }] = useLazyQuery(GET_RATINGS_BY_COHORT, { + fetchPolicy: 'network-only', + }) + const [getTeamsByCohort, {data: teams, loading: loadingTeams, error: teamsError}] = useLazyQuery(GET_TEAMS_BY_COHORT, { + fetchPolicy: 'network-only', + }) + const [addRatingsByFile, { data: bulkRatings, loading: loadingBulkRatings, error: bulkRatingsError }] = useMutation(ADD_RATINGS_BY_FILE) + + const [selectedTeam, setSelectedTeam] = useState('') + const fileUploadRef = useRef(null) + + const saveRatings = async (e: React.FormEvent) => { + try { + e.preventDefault() + if(!formData.cohortId) throw new Error("Please select a cohort") + if(!formData.sprint) throw new Error("Please select a sprint") + if(!formData.file) throw new Error("Please select a file") + await addRatingsByFile({ + variables: { + file: formData.file, + cohortId: formData.cohortId, + sprint: parseInt(formData.sprint, 10), + orgToken: localStorage.getItem('orgToken') + }, + }) + await getRatingsByCohort({ + variables: { + cohortId: formData.cohortId, + orgToken: localStorage.getItem('orgToken') + } + }) + toast.success("Rating completed successfully") + if(fileUploadRef.current){ + fileUploadRef.current.value = '' + setFormData({...formData, file: null}) + } + } catch (err: any) { + toast.error(err?.message) + if(fileUploadRef.current){ + fileUploadRef.current.value = '' + setFormData({...formData, file: null}) + } + } + } + + const downloadTeamFile = async(e: any)=>{ + try{ + if(selectedTeam === '') throw new Error("No Team was selected") + const team = teams.getTeamsByCohort.find((team:any)=>team.id === selectedTeam) + const rows: any = [] + team.members.forEach((member: any) => { + if (member.role === "trainee") { + rows.push({ + email: member.email, + quantity: '', + quality: '', + professional_skills: '', + feedBacks: '' + }) + } + }) + const workSheet = rows.length ? XLSX.utils.json_to_sheet(rows) : XLSX.utils.json_to_sheet([{ + email: '', + quantity: '', + quality: '', + professional_skills:'', + feedBacks: '' + }]) + const workBook = XLSX.utils.book_new() + workSheet["!cols"] = [ { wch: 20 } ] + XLSX.utils.book_append_sheet(workBook, workSheet,"ratings") + XLSX.writeFile(workBook, `${team.name.replace(' ','')}_Ratings.xlsx`) + }catch(err: any){ + toast.error(err?.message) + } + } + + const selectCohort= async(e: React.ChangeEvent)=>{ + try{ + setFormData({...formData, cohortId: e.target.value }) + await getRatingsByCohort({ + variables: { + cohortId: e.target.value, + orgToken: localStorage.getItem('orgToken') + } + }) + await getTeamsByCohort({ + variables: { + cohortId: e.target.value, + orgToken: localStorage.getItem('orgToken') + } + }) + }catch(err: any){ + toast.error(err?.message) + } + } + + useEffect(() => { + getUserCohorts() + getRatingsByCohort() + }, []) + + return ( +
+
+
+

+ {t('Bulk Rating')} +

+
+
+
+
+
+ + + + +
+
+ { + const file = e.target.files?.[0] + setFormData({ ...formData, file: file ? file : null }) + }} + accept=".xlsx, .xls" + > + +
+ + +
+
+ +
+ { + bulkRatings && bulkRatings.addRatingsByFile.RejectedRatings.length > 0 ? +
+ + + + + + + + + + + + + {bulkRatings.addRatingsByFile?.RejectedRatings.map((rating: any, index: number) => + + + + + + + + )} + +
+ Rejected Ratings +
EmailQuantityQualityProfessional_SkillsFeedback
{rating.email ? rating.email : "No Value"}{rating.quantity !== null ? rating.quantity : "No Value"}{rating.quality !== null ? rating.quality : "No Value"}{rating.professional_skills !== null ? rating.professional_skills : "No Value"}{rating.feedBacks ? rating.feedBacks : "No Value"}
+
+ : '' + } +
+
+ + +
+
+
+
+
+ ) +} + +export default BulkRatingModal \ No newline at end of file diff --git a/src/pages/AdminTraineeDashboard.tsx b/src/pages/AdminTraineeDashboard.tsx index 4b07ae553..aa2bc7c78 100644 --- a/src/pages/AdminTraineeDashboard.tsx +++ b/src/pages/AdminTraineeDashboard.tsx @@ -42,6 +42,7 @@ import Dropdown from 'react-dropdown-select'; import ViewWeeklyRatings from '../components/ratings/ViewWeeklyRatings'; import { FaTimes } from 'react-icons/fa'; import TtlSkeleton from '../Skeletons/ttl.skeleton'; +import BulkRatingModal from '../components/BulkRatingModal'; const organizationToken = localStorage.getItem('orgToken'); function AdminTraineeDashboard() { @@ -92,6 +93,9 @@ function AdminTraineeDashboard() { // restoreMemberFromCohort const [selectedTraineeId, setSelectedTraineeId] = useState(); + //BulkRatingModal + const [bulkRateModal, setBulkRateModal] = useState(false) + useEffect(() => { const handleClickOutside = (event: any) => { if (modalRef.current && !modalRef.current.contains(event.target)) { @@ -1583,7 +1587,12 @@ function AdminTraineeDashboard() { {/* =========================== End:: RemoveTraineeModel =============================== */} - + {/*============================ Start:: BulkRateModal =================================== */} + + {/*============================ End:: BulkRateModal =================================== */}
@@ -1600,6 +1609,18 @@ function AdminTraineeDashboard() { > {t('add')} +{' '} + { + JSON.parse(localStorage.getItem('auth')!) && ['coordinator','ttl'].includes(JSON.parse(localStorage.getItem('auth')!).role) ? + : '' + }
diff --git a/src/pages/ttlTraineeDashboard.tsx b/src/pages/ttlTraineeDashboard.tsx index 1827294f5..9474c54a0 100644 --- a/src/pages/ttlTraineeDashboard.tsx +++ b/src/pages/ttlTraineeDashboard.tsx @@ -28,6 +28,7 @@ import { FaTimes } from 'react-icons/fa'; import { log } from 'console'; import { FaEye } from 'react-icons/fa'; +import BulkRatingModal from '../components/BulkRatingModal'; const organizationToken = localStorage.getItem('orgToken'); ``; /* istanbul ignore next */ @@ -60,6 +61,7 @@ const TtlTraineeDashboard = () => { const [open2, setOpen2] = React.useState(false); const [selectedTraineeId, setSelectedTraineeId] = useState() + const [bulkRateModal, setBulkRateModal] = useState(false) const handleClickOpen2 = async () => { setIsLoaded(true); @@ -418,6 +420,17 @@ const TtlTraineeDashboard = () => {
+
+ +
{fetchError || traineeData?.length === 0 ? ( // Check both fetchError and traineeData length { )}
+ { + bulkRateModal? + : '' + }
diff --git a/tests/components/BulkRatingModal.test.tsx b/tests/components/BulkRatingModal.test.tsx new file mode 100644 index 000000000..e2530b232 --- /dev/null +++ b/tests/components/BulkRatingModal.test.tsx @@ -0,0 +1,312 @@ +import React from "react" +import "@testing-library/jest-dom" +import { MockedProvider, MockedResponse } from "@apollo/client/testing" +import { render, fireEvent, screen, cleanup, waitFor } from "@testing-library/react" +import BulkRatingModal from "../../src/components/BulkRatingModal" +import { ADD_RATINGS_BY_FILE, GET_RATINGS_BY_COHORT } from "../../src/Mutations/Ratings" +import { GET_TEAMS_BY_COHORT } from "../../src/Mutations/teamMutation" +import { toast } from "react-toastify" +import * as XLSX from "xlsx" +import { GET_USER_COHORTS } from "../../src/Mutations/cohortMutations" + +const mockRatingsFile: File = new File(["Test"], "testRatings.xlsx", {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}) + +const getUserCohorts: MockedResponse = { + request:{ + query: GET_USER_COHORTS, + variables: { + orgToken: "mocked_org_token", + } + }, + result:{ + data:{ + getUserCohorts: [ + { + id: "1", + name: "Cohort 1", + }, + { + id: "2", + name: "Cohort 2" + } + ] + } + } +} + +const getRatingsByCohort: MockedResponse = { + request:{ + query: GET_RATINGS_BY_COHORT, + variables: { + cohortId: "1", + orgToken: "mocked_org_token", + } + }, + result:{ + data: { + getRatingsByCohort: [ + { + id: "1", + sprint: 1, + }, + { + id: "2", + sprint: 2, + } + ] + } + } +} + +const getTeamsByCohort = { + request: { + query: GET_TEAMS_BY_COHORT, + variables: { + cohortId: "1", + orgToken: "mocked_org_token" + } + }, + result:{ + data: { + getTeamsByCohort: [ + { + id: "1", + name: "Team I", + members:[ + { + email: "test@gmail.com", + role: "trainee" + }, + { + email: "test2@gmail.com", + role: "ttl" + }, + { + email: "test3@gmail.com", + role: "trainee" + }, + ] + }, + { + id: "2", + name: "Team II", + members:[ + { + email: "test4@gmail.com", + role: "trainee" + }, + { + email: "test5@gmail.com", + role: "ttl" + }, + { + email: "test6@gmail.com", + role: "trainee" + }, + ] + } + ] + } + } +} + +const addRatingsByFile: MockedResponse = { + request:{ + query: ADD_RATINGS_BY_FILE, + variables: { + file: mockRatingsFile, + cohortId: "1", + sprint: 1, + orgToken: "mocked_org_token" + } + }, + result:{ + data:{ + addRatingsByFile: { + NewRatings: { + user: { + email: "test@gmail.com" + }, + sprint: 1, + phase: "Phase I", + quality: 1, + quantity: 1, + professional_Skills: 1, + feedbacks: { + sender: { + email: "testing@gmail.com" + }, + content: "ok", + createdAt: "2024-10-30T00:00:00:000z" + }, + cohort: { + name: "Cohort 1" + }, + }, + RejectedRatings:{ + email: "rejectedemail@gmail.com", + quantity: 1, + quality: 1, + professional_skills: 1, + feedBacks: "ok", + }, + UpdatedRatings: { + quantity: 1, + quality: 1, + professional_Skills: 1, + feedbacks: { + content: "Average" + }, + oldFeedback: ["ok"], + } + } + } + } +} + +jest.mock('react-toastify', () => ({ + toast: { + success: jest.fn(), + error: jest.fn(), + } +})) + +jest.mock('xlsx') + +beforeEach(() => { + localStorage.setItem('auth_token', 'mocked_auth_token') + localStorage.setItem('orgToken', 'mocked_org_token') + localStorage.setItem('auth', JSON.stringify({ + auth: true, + email: "testing@gmail.com", + firstName: "Jack", + role: "coordinator", + userId: "1" + })) +}) + +afterEach(() => { + localStorage.clear() + cleanup() +}) + +describe("BulkRatingModal", () => { + const setBulkRateModel = jest.fn() + it("displays all cohorts", async() => { + render( + + + + ) + await waitFor(()=>{ + expect(screen.getByTestId("cohort-option-1")).toBeInTheDocument() + expect(screen.getByTestId("cohort-option-2")).toBeInTheDocument() + }) + }) + it("displays all sprints", async() => { + render( + + + + ) + const cohortInput = screen.getByTestId("select-cohort") + await waitFor(()=>{ + fireEvent.change(cohortInput, { + target: { + value: "1" + } + }) + expect(screen.getByTestId("sprint-option-1")).toBeInTheDocument() + expect(screen.getByTestId("sprint-option-2")).toBeInTheDocument() + }) + }) + it("displays teams", async() => { + render( + + + + ) + const cohortInput = screen.getByTestId("select-cohort") + await waitFor(()=>{ + fireEvent.change(cohortInput, { + target: { + value: "1" + } + }) + expect(screen.getByTestId("team-option-1")).toBeInTheDocument() + expect(screen.getByTestId("team-option-2")).toBeInTheDocument() + }) + }) + + it("bulk rates successfully", async() => { + render( + + + + ) + const bulkRatingForm = screen.getByTestId("bulk-rating-form") + const cohortInput = screen.getByTestId("select-cohort") + const sprintInput = screen.getByTestId("select-sprint") + const fileInput = screen.getByTestId("file-input") + await waitFor(()=>{ + fireEvent.change(cohortInput, { + target: { + value: "1" + } + }) + fireEvent.change(sprintInput, { + target: { + value: "1" + } + }) + fireEvent.change(fileInput, { + target: { + files: [mockRatingsFile] + } + }) + fireEvent.submit(bulkRatingForm) + expect(toast.success).toHaveBeenCalledWith("Rating completed successfully") + }) + }) + + it("downloads team rating templates successfully", async() => { + render( + + + + ) + const cohortInput = screen.getByTestId("select-cohort") + const teamInput = screen.getByTestId("select-team") + const downloadButton = screen.getByTestId("download-button") + await waitFor(()=>{ + fireEvent.change(cohortInput, { + target: { + value: "1" + } + }) + fireEvent.change(teamInput, { + target: { + value: "1" + } + }) + fireEvent.click(downloadButton) + expect(XLSX.utils.json_to_sheet).toHaveBeenCalled() + expect(XLSX.utils.book_new).toHaveBeenCalled() + }) + }) +}) \ No newline at end of file diff --git a/tests/components/__snapshots__/AdminTraineeDashboard.test.tsx.snap b/tests/components/__snapshots__/AdminTraineeDashboard.test.tsx.snap index 3d11fe910..49feec9eb 100644 --- a/tests/components/__snapshots__/AdminTraineeDashboard.test.tsx.snap +++ b/tests/components/__snapshots__/AdminTraineeDashboard.test.tsx.snap @@ -463,7 +463,7 @@ Array [ name="date" readOnly={true} type="text" - value="2024-10-24" + value="2024-10-31" />
, +
+
+
+

+ Bulk Rating +

+
+
+
+
+
+ + + + +
+
+ +
+ + +
+
+
+
+ + +
+ +
+
+
,
diff --git a/tests/pages/__snapshots__/About.test.tsx.snap b/tests/pages/__snapshots__/About.test.tsx.snap index 539be8335..7df40910f 100644 --- a/tests/pages/__snapshots__/About.test.tsx.snap +++ b/tests/pages/__snapshots__/About.test.tsx.snap @@ -170,7 +170,6 @@ exports[`About page renders the about page 1`] = ` > Come shape the future together -
- I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

- I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

- - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

- I'm extremely impressed with Pulse and their performance management platform. - Since using their services, it has been a game-changer for our organization. - The platform is intuitive, easy to navigate, and packed with powerful features. + Content1

- I'm delighted to share my positive experience with Pulse and their exceptional - performance management platform. Implementing their services has led to remarkable - improvements in our performance tracking and management processes. + Content2

- - We are thrilled with the services provided by Pulse. Their performance management platform - has exceeded our expectations in every way. The user-friendly interface and comprehensive - features have made tracking and monitoring our performance metrics a breeze. - + Content3

diff --git a/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap b/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap index e3a616220..1b3c51a92 100644 --- a/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap +++ b/tests/pages/__snapshots__/AdminTraineeDashboard.test.tsx.snap @@ -463,7 +463,7 @@ Array [ name="date" readOnly={true} type="text" - value="2024-10-24" + value="2024-10-31" />
, +
+
+
+

+ Bulk Rating +

+
+
+
+
+
+ + + + +
+
+ +
+ + +
+
+
+
+ + +
+ +
+
+
,
diff --git a/tests/pages/__snapshots__/GradingSystem.test.tsx.snap b/tests/pages/__snapshots__/GradingSystem.test.tsx.snap index 6dc4578ab..08d39d3a7 100644 --- a/tests/pages/__snapshots__/GradingSystem.test.tsx.snap +++ b/tests/pages/__snapshots__/GradingSystem.test.tsx.snap @@ -369,20 +369,20 @@ Array [ className="max-w-full" >

Gradings List

@@ -401,7 +401,7 @@ Array [ role="row" > @@ -456,7 +453,7 @@ Array [ colSpan={3} >

No records available

@@ -466,7 +463,7 @@ Array [
Names - action - More -

Performance Ratings

@@ -151,7 +151,7 @@ exports[`TraineeRatingDashboard Tests Renders TraineeRatingDashboard 1`] = ` role="row" > @@ -296,7 +287,7 @@ exports[`TraineeRatingDashboard Tests Renders TraineeRatingDashboard 1`] = ` colSpan={9} >

No records available

@@ -304,7 +295,7 @@ exports[`TraineeRatingDashboard Tests Renders TraineeRatingDashboard 1`] = `
Email - Phase - Sprint - Quantity - Quality - Professional skills - Average - Actions - Remarks -
Loading... @@ -314,7 +305,7 @@ exports[`TraineeRatingDashboard Tests Renders TraineeRatingDashboard 1`] = `

Performance Ratings

@@ -178,7 +178,7 @@ Array [ role="row" > @@ -293,7 +286,7 @@ Array [ colSpan={7} >

No records available

@@ -303,7 +296,7 @@ Array [
Name - Sprint - Quantity - Quality - Professional skills - Feedback - Actions -