From 7edf69951b52eee165841b5fc060e2270ecff6c5 Mon Sep 17 00:00:00 2001 From: Sam Bley Date: Thu, 14 Dec 2023 19:13:42 -0600 Subject: [PATCH] allow selected user's whole chain of command to request feedback on their behalf --- .../resources/db/dev/R__Load_testing_data.sql | 4 +- web-ui/src/context/selectors.js | 28 +++++--- web-ui/src/context/selectors.test.js | 70 ++++++++++++++++++- web-ui/src/pages/MemberProfilePage.jsx | 5 +- 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/server/src/main/resources/db/dev/R__Load_testing_data.sql b/server/src/main/resources/db/dev/R__Load_testing_data.sql index ec31570346..66a317d36d 100644 --- a/server/src/main/resources/db/dev/R__Load_testing_data.sql +++ b/server/src/main/resources/db/dev/R__Load_testing_data.sql @@ -52,7 +52,7 @@ VALUES INSERT INTO member_profile -- Michael Kimberlin (id, firstName, lastName, title, pdlid, location, workEmail, employeeid, startdate, biotext, supervisorid) VALUES -('6207b3fd-042d-49aa-9e28-dcc04f537c2d', PGP_SYM_ENCRYPT('Michael','${aeskey}'), PGP_SYM_ENCRYPT('Kimberlin','${aeskey}'), PGP_SYM_ENCRYPT('Director of Organizational Development','${aeskey}'), '8fa673c0-ca19-4271-b759-41cb9db2e83a', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('kimberlinm@objectcomputing.com','${aeskey}'), '12312342', '2012-09-29', PGP_SYM_ENCRYPT('Developer of developers and others','${aeskey}'), null); +('6207b3fd-042d-49aa-9e28-dcc04f537c2d', PGP_SYM_ENCRYPT('Michael','${aeskey}'), PGP_SYM_ENCRYPT('Kimberlin','${aeskey}'), PGP_SYM_ENCRYPT('Director of Organizational Development','${aeskey}'), '8fa673c0-ca19-4271-b759-41cb9db2e83a', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('kimberlinm@objectcomputing.com','${aeskey}'), '12312342', '2012-09-29', PGP_SYM_ENCRYPT('Developer of developers and others','${aeskey}'), '01b7d769-9fa2-43ff-95c7-f3b950a27bf9'); INSERT INTO member_profile -- Mark Volkmann (id, firstName, lastName, title, location, workEmail, employeeid, startdate, biotext, supervisorid) @@ -82,7 +82,7 @@ VALUES INSERT INTO member_profile -- Zack Brown (id, firstName, lastName, title, pdlid, location, workEmail, employeeid, startdate, biotext, supervisorid) VALUES -('43ee8e79-b33d-44cd-b23c-e183894ebfef', PGP_SYM_ENCRYPT('Zack','${aeskey}'), PGP_SYM_ENCRYPT('Brown','${aeskey}'), PGP_SYM_ENCRYPT('Software Engineer','${aeskey}'), '2559a257-ae84-4076-9ed4-3820c427beeb', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('brownz@objectcomputing.com','${aeskey}'), '123123412', '2012-09-29', PGP_SYM_ENCRYPT('Engineer Phenomenal','${aeskey}'), null); +('43ee8e79-b33d-44cd-b23c-e183894ebfef', PGP_SYM_ENCRYPT('Zack','${aeskey}'), PGP_SYM_ENCRYPT('Brown','${aeskey}'), PGP_SYM_ENCRYPT('Software Engineer','${aeskey}'), '2559a257-ae84-4076-9ed4-3820c427beeb', PGP_SYM_ENCRYPT('St. Louis','${aeskey}'), PGP_SYM_ENCRYPT('brownz@objectcomputing.com','${aeskey}'), '123123412', '2012-09-29', PGP_SYM_ENCRYPT('Engineer Phenomenal','${aeskey}'), '6207b3fd-042d-49aa-9e28-dcc04f537c2d'); INSERT INTO member_profile -- Joe Warner (id, firstName, lastName, title, pdlid, location, workEmail, employeeid, startdate, biotext, supervisorid) diff --git a/web-ui/src/context/selectors.js b/web-ui/src/context/selectors.js index cfc8513e25..4391af4147 100644 --- a/web-ui/src/context/selectors.js +++ b/web-ui/src/context/selectors.js @@ -232,18 +232,26 @@ export const selectPDLCheckinMap = createSelector(selectCheckins, (checkins) => export const selectSupervisors = createSelector( selectCurrentMembers, selectProfileMap, - (currentMembers, memberProfileMap) => currentMembers?.reduce((supervisors, currentMember) => { - let supervisorId = currentMember.supervisorid; + (currentMembers, memberProfileMap) => { + const filteredMembers = currentMembers?.filter(member => member.supervisorid); - const inSupervisors = supervisors.find( - (supervisor) => supervisorId === supervisor?.id - ) - - if (!inSupervisors){ - supervisors.push(memberProfileMap[supervisorId]); - } + const supervisorIds = filteredMembers?.map(member => member.supervisorid); + const uniqueSupervisorIds = [...new Set(supervisorIds)]; + + const supervisors = uniqueSupervisorIds.map(id => memberProfileMap[id]); return supervisors; - }, []) + } +); + +const buildSupervisorHierarchy = (allSupervisors, member, supervisorChain ) => { + const memberSupervisor = allSupervisors?.find(supervisor => supervisor.id === member?.supervisorid); + supervisorChain.push(memberSupervisor); + return !memberSupervisor?.supervisorid ? supervisorChain : buildSupervisorHierarchy(allSupervisors, memberSupervisor, supervisorChain); +} + +export const selectSupervisorHierarchyIds = (selectedMember) => createSelector( + selectSupervisors, + (allSupervisors) => buildSupervisorHierarchy(allSupervisors, selectedMember, []).map(supervisor => supervisor?.id) ); export const selectIsSupervisor = createSelector( diff --git a/web-ui/src/context/selectors.test.js b/web-ui/src/context/selectors.test.js index 0c0efceb97..315da1e375 100644 --- a/web-ui/src/context/selectors.test.js +++ b/web-ui/src/context/selectors.test.js @@ -10,7 +10,7 @@ import { selectCurrentMembers, selectNormalizedMembers, selectNormalizedTeams, - selectMostRecentCheckin, selectSupervisors, + selectMostRecentCheckin, selectSupervisors, selectSupervisorHierarchyIds, } from "./selectors"; describe("Selectors", () => { @@ -867,6 +867,13 @@ it("selectSupervisors should return only members who are supervisors", () => { lastName: "Boss", supervisorid: 1, }, + { + id: 6, + employeeId: 15, + name: "No Supervisor", + firstName: "No", + lastName: "Supervisor", + }, ] const testState = { @@ -877,3 +884,64 @@ it("selectSupervisors should return only members who are supervisors", () => { expect(selectSupervisors(testState)).toEqual(expectedResult); }); + +it("selectSupervisorHierarchyIds should return a list of ids of everyone who is above the selected member", () => { + const testMemberProfiles = [ + { + id: 1, + employeeId: 11, + name: "Big Boss", + firstName: "Big", + lastName: "Boss", + supervisorid: 2, + }, + { + id: 2, + employeeId: 12, + name: "Huey Emmerich", + firstName: "Huey", + lastName: "Emmerich", + supervisorid: 4, + }, + { + id: 3, + employeeId: 13, + name: "Kazuhira Miller", + firstName: "Kazuhira", + lastName: "Miller", + supervisorid: 5, + }, + { + id: 4, + employeeId: 14, + name: "Revolver Ocelot", + firstName: "Revolver", + lastName: "Ocelot", + supervisorid: 6, + }, + { + id: 5, + employeeId: 15, + name: "The Boss", + firstName: "The", + lastName: "Boss", + supervisorid: 6, + }, + { + id: 6, + employeeId: 15, + name: "No Supervisor", + firstName: "No", + lastName: "Supervisor", + }, + ] + + const testState = { + memberProfiles: testMemberProfiles + }; + + const expectedResult = [testMemberProfiles[1].id, testMemberProfiles[3].id, testMemberProfiles[5].id]; + + expect(selectSupervisorHierarchyIds(testMemberProfiles[0])(testState)).toEqual(expectedResult); +}); + diff --git a/web-ui/src/pages/MemberProfilePage.jsx b/web-ui/src/pages/MemberProfilePage.jsx index 45276ab933..19d34d4fb3 100644 --- a/web-ui/src/pages/MemberProfilePage.jsx +++ b/web-ui/src/pages/MemberProfilePage.jsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; import { useParams } from "react-router-dom"; -import { selectCurrentUserId, selectIsAdmin, selectProfile, selectTerminatedMembers } from "../context/selectors"; +import { selectCurrentUserId, selectIsAdmin, selectProfile, selectTerminatedMembers, selectSupervisorHierarchyIds } from "../context/selectors"; import { AppContext } from "../context/AppContext"; import { getSelectedMemberSkills } from "../api/memberskill"; import { getTeamByMember } from "../api/team"; @@ -42,8 +42,9 @@ const MemberProfilePage = () => { const currentUserId = selectCurrentUserId(state); const pdlInfo = sortedPdls && sortedPdls.find((pdl) => pdl?.id === selectedMember?.pdlId); const supervisorInfo = sortedMembers && sortedMembers.find((memberProfile) => memberProfile?.id === selectedMember?.supervisorid); + const supervisorChain = selectSupervisorHierarchyIds(selectedMember)(state); const currentUserIsPdl = pdlInfo?.id === currentUserId; - const currentUserIsSupervisor = supervisorInfo?.id === currentUserId; + const currentUserIsSupervisor = supervisorChain.includes(currentUserId); const canRequestFeedback = isAdmin || currentUserIsPdl || currentUserIsSupervisor;