diff --git a/server/build.gradle b/server/build.gradle index 4aa56e47b2..b98fc730da 100755 --- a/server/build.gradle +++ b/server/build.gradle @@ -7,7 +7,7 @@ plugins { id "jacoco" } -version "0.8.15" +version "0.8.16" group "com.objectcomputing.checkins" repositories { @@ -63,7 +63,7 @@ configurations { } dependencies { - runtimeOnly("org.flywaydb:flyway-database-postgresql") + runtimeOnly("org.flywaydb:flyway-database-postgresql:11.3.4") runtimeOnly("org.yaml:snakeyaml") runtimeOnly("ch.qos.logback:logback-classic") runtimeOnly("org.postgresql:postgresql") diff --git a/web-ui/package.json b/web-ui/package.json index 1f396ae4e2..9478e75642 100644 --- a/web-ui/package.json +++ b/web-ui/package.json @@ -1,6 +1,6 @@ { "name": "web-ui", - "version": "0.8.15", + "version": "0.8.16", "private": true, "type": "module", "dependencies": { @@ -20,7 +20,7 @@ "@mui/styles": "^5.15.14", "@mui/x-date-pickers": "^7.3.1", "@tinymce/tinymce-react": "^5.0.0", - "axios": "^1.7.4", + "axios": "^1.8.2", "canvas-confetti": "^1.6.0", "date-fns": "^2.24.0", "dayjs": "^1.11.11", diff --git a/web-ui/src/api/roles.js b/web-ui/src/api/roles.js index 3af15f2b85..f5d4e4654d 100644 --- a/web-ui/src/api/roles.js +++ b/web-ui/src/api/roles.js @@ -9,14 +9,14 @@ export const getAllRoles = async cookie => { }); }; -export const getAllUserRoles = async cookie => { +export const getAllMemberRoles = async cookie => { return resolve({ url: roleURL + '/members', headers: { 'X-CSRF-Header': cookie, Accept: 'application/json' } }); }; -export const removeUserFromRole = async (roleId, memberId, cookie) => { +export const removeMemberFromRole = async (roleId, memberId, cookie) => { return resolve({ method: 'DELETE', url: roleURL + `/members/${roleId}/${memberId}`, @@ -28,7 +28,7 @@ export const removeUserFromRole = async (roleId, memberId, cookie) => { }); }; -export const addUserToRole = async (roleId, memberId, cookie) => { +export const addMemberToRole = async (roleId, memberId, cookie) => { return resolve({ method: 'POST', url: roleURL + '/members', diff --git a/web-ui/src/components/action_item/ActionItemsPanel.test.jsx b/web-ui/src/components/action_item/ActionItemsPanel.test.jsx index 5f00dcf947..af97d1650d 100644 --- a/web-ui/src/components/action_item/ActionItemsPanel.test.jsx +++ b/web-ui/src/components/action_item/ActionItemsPanel.test.jsx @@ -29,7 +29,7 @@ const initialState = { teams: [], skills: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], memberProfiles: [] } diff --git a/web-ui/src/components/admin/roles/Roles.jsx b/web-ui/src/components/admin/roles/Roles.jsx index 3145163270..1f3865581f 100644 --- a/web-ui/src/components/admin/roles/Roles.jsx +++ b/web-ui/src/components/admin/roles/Roles.jsx @@ -3,18 +3,18 @@ import React, { useContext, useEffect, useState } from 'react'; import { AppContext } from '../../../context/AppContext'; import { SET_ROLES, - SET_USER_ROLES, + SET_MEMBER_ROLES, UPDATE_TOAST } from '../../../context/actions'; import { - addUserToRole, + addMemberToRole, addNewRole, - removeUserFromRole, + removeMemberFromRole, updateRole } from '../../../api/roles'; import { selectCanEditMemberRolesPermission, - noPermission, + noPermission, selectMemberRoles, selectCsrfToken, selectRoles, selectMemberProfiles, } from '../../../context/selectors'; import RoleUserCards from './RoleUserCards'; @@ -52,8 +52,11 @@ import './Roles.css'; const Roles = () => { const { state, dispatch } = useContext(AppContext); - // roles here is all possible roles, not the selected roles. - const { csrf, memberProfiles, roles, userRoles } = state; + + const csrf = selectCsrfToken(state); + const memberProfiles = selectMemberProfiles(state); + const roles = selectRoles(state); // all possible roles, not the selected roles. + const memberRoles = selectMemberRoles(state); const [showAddUser, setShowAddUser] = useState(false); const [showEditRole, setShowEditRole] = useState(false); @@ -97,25 +100,25 @@ const Roles = () => { } const newRoleToMemberMap = {}; - for (const userRole of userRoles || []) { + for (const memberRole of memberRoles || []) { const role = roles.find( - role => role.id === userRole?.memberRoleId?.roleId + role => role.id === memberRole?.memberRoleId?.roleId ); if (role) { let memberList = newRoleToMemberMap[role.role]; if (!memberList) { memberList = newRoleToMemberMap[role.role] = []; } - if (memberMap[userRole?.memberRoleId?.memberId] !== undefined) { + if (memberMap[memberRole?.memberRoleId?.memberId] !== undefined) { memberList.push({ - ...memberMap[userRole?.memberRoleId?.memberId], + ...memberMap[memberRole?.memberRoleId?.memberId], roleId: role.id }); } } } setRoleToMemberMap(newRoleToMemberMap); - }, [userRoles, memberProfiles, roles]); + }, [memberRoles, memberProfiles, roles]); const getRoleStats = role => { let members = roleToMemberMap[role]; @@ -125,20 +128,20 @@ const Roles = () => { const removeFromRole = async (member, role) => { const members = roleToMemberMap[role]; const { roleId } = members.find(m => member.id === m.id); - let res = await removeUserFromRole(roleId, member.id, csrf); + let res = await removeMemberFromRole(roleId, member.id, csrf); let data = res.payload && res.payload.status === 200 && !res.error ? res.payload : null; if (data) { // TODO: Remove role from map.... - const filtered = userRoles.filter( - userRole => - userRole?.memberRoleId?.roleId !== roleId || - userRole?.memberRoleId?.memberId !== member.id + const filtered = memberRoles.filter( + memberRole => + memberRole?.memberRoleId?.roleId !== roleId || + memberRole?.memberRoleId?.memberId !== member.id ); dispatch({ - type: SET_USER_ROLES, + type: SET_MEMBER_ROLES, payload: filtered }); window.snackDispatch({ @@ -153,14 +156,14 @@ const Roles = () => { const addToRole = async member => { const role = roles.find(role => role.role === currentRole.role); - let res = await addUserToRole(role.id, member.id, csrf); + let res = await addMemberToRole(role.id, member.id, csrf); let data = res.payload && res.payload.data && !res.error ? res.payload.data : null; if (data) { setShowAddUser(false); dispatch({ - type: SET_USER_ROLES, - payload: [...userRoles, data] + type: SET_MEMBER_ROLES, + payload: [...memberRoles, data] }); window.snackDispatch({ type: UPDATE_TOAST, diff --git a/web-ui/src/components/admin/roles/Roles.test.jsx b/web-ui/src/components/admin/roles/Roles.test.jsx index 8f90beddc5..d36cab40e4 100644 --- a/web-ui/src/components/admin/roles/Roles.test.jsx +++ b/web-ui/src/components/admin/roles/Roles.test.jsx @@ -9,18 +9,49 @@ const initialState = { { id: 2, name: 'Señora Test' }, { id: 3, name: 'Herr Test' } ], + memberRoles: [ + { memberRoleId: { roleId: 1, memberId: 1 } }, + { memberRoleId: { roleId: 2, memberId: 2 } }, + ], roles: [ - { id: 1, role: 'ADMIN', memberid: 1 }, - { id: 2, role: 'PDL', memberid: 2 } + { id: 1, role: 'ADMIN', description: 'Administrator' }, + { id: 2, role: 'PDL', description: 'Prof. Dev. Lead' }, + { id: 3, role: 'MEMBER', description: 'A member of the org' } ], userProfile: { name: 'Current User', role: ['MEMBER'], + id: 1, permissions: [{ permission: 'CAN_EDIT_MEMBER_ROLES' }], }, } }; +const noPermState = { + state: { + memberProfiles: [ + { id: 1, name: 'Señior Test' }, + { id: 2, name: 'Señora Test' }, + { id: 3, name: 'Herr Test' } + ], + memberRoles: [ + { memberRoleId: { roleId: 1, memberId: 1 } }, + { memberRoleId: { roleId: 2, memberId: 2 } }, + ], + roles: [ + { id: 1, role: 'ADMIN', description: 'Administrator' }, + { id: 2, role: 'PDL', description: 'Prof. Dev. Lead' }, + { id: 3, role: 'MEMBER', description: 'A member of the org' } + ], + userProfile: { + name: 'Current User', + role: ['MEMBER'], + id: 1, + permissions: [], + }, + } +}; + it('renders correctly', () => { snapshot( @@ -31,7 +62,7 @@ it('renders correctly', () => { it('renders an error if user does not have appropriate permission', () => { snapshot( - + ); diff --git a/web-ui/src/components/admin/roles/__snapshots__/Roles.test.jsx.snap b/web-ui/src/components/admin/roles/__snapshots__/Roles.test.jsx.snap index 024be00164..55c6dd895d 100644 --- a/web-ui/src/components/admin/roles/__snapshots__/Roles.test.jsx.snap +++ b/web-ui/src/components/admin/roles/__snapshots__/Roles.test.jsx.snap @@ -47,14 +47,14 @@ exports[`renders correctly 1`] = ` role="combobox" tabindex="0" > - ADMIN, PDL + ADMIN, MEMBER, PDL
+ > + Administrator +
- 0 + 1 Users
@@ -260,6 +262,57 @@ exports[`renders correctly 1`] = ` class="MuiDivider-root MuiDivider-fullWidth css-9mgopn-MuiDivider-root" /> +
+
  • +
    +
    + Señior Test's avatar +
    +
    +
    +

    + Señior Test +

    +

    +

    +
    + +
    +
  • +
    @@ -296,7 +349,156 @@ exports[`renders correctly 1`] = `
    + Prof. Dev. Lead +
    +
    + 1 + Users +
    + +
    + + +
    + +
    + +
    +
  • +
    +
    + Señora Test's avatar +
    +
    +
    +

    + Señora Test +

    +

    +

    +
    + +
    +
  • +
    + + + +
    +
    +
    +
    +
      +
      +
    • +
      +
      +

      + MEMBER +

      +
      + A member of the org +
      { const { state } = useContext(AppContext); const { csrf, selectedProfile, userProfile } = state; - const { name, pdlId, title, workEmail } = selectedProfile - ? selectedProfile - : userProfile && userProfile.memberProfile - ? userProfile.memberProfile - : {}; + const currentUserProfile = selectCurrentUser(state); + const { name, pdlId, title, workEmail } = selectedProfile ? selectedProfile : currentUserProfile; const [pdl, setPDL] = useState(); // Get PDL's name diff --git a/web-ui/src/components/checkin/documents/CheckinDocs.test.jsx b/web-ui/src/components/checkin/documents/CheckinDocs.test.jsx index 98753d6479..db9b98ba2e 100644 --- a/web-ui/src/components/checkin/documents/CheckinDocs.test.jsx +++ b/web-ui/src/components/checkin/documents/CheckinDocs.test.jsx @@ -24,15 +24,21 @@ const initialState = { ], currentCheckin: [{ id: '10-982409128354' }], userProfile: { - memberProfile: { - id: '130u410234' - }, + id: '130u410234', name: 'holmes', role: ['PDL'], permissions: [{ permission: 'CAN_ADMINISTER_CHECKIN_DOCUMENTS' }], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' }, + memberProfiles: [ + { + id: '130u410234', + name: 'holmes', + imageUrl: + 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' + } + ], index: 0 } }; diff --git a/web-ui/src/components/discrete_slider/DiscreteSlider.test.tsx b/web-ui/src/components/discrete_slider/DiscreteSlider.test.tsx index cec6492351..31d955c100 100644 --- a/web-ui/src/components/discrete_slider/DiscreteSlider.test.tsx +++ b/web-ui/src/components/discrete_slider/DiscreteSlider.test.tsx @@ -4,8 +4,8 @@ import DiscreteSlider from './DiscreteSlider'; vi.mock('@mui/material/Slider', () => { return { - default: () => (props: any) => { - const { onChange, 'data-testid': testId, ...rest } = props; + default: (props: any) => { + const { onChange, 'data-testid': testId } = props; return ( { onChange={event => { onChange(null, parseInt(event.target.value, 10)); }} - {...rest} /> ); } diff --git a/web-ui/src/components/discrete_slider/__snapshots__/DiscreteSlider.test.tsx.snap b/web-ui/src/components/discrete_slider/__snapshots__/DiscreteSlider.test.tsx.snap index 34e4af06e8..8c099ebf74 100644 --- a/web-ui/src/components/discrete_slider/__snapshots__/DiscreteSlider.test.tsx.snap +++ b/web-ui/src/components/discrete_slider/__snapshots__/DiscreteSlider.test.tsx.snap @@ -13,7 +13,12 @@ exports[`DiscreteSlider > renders slider with title 1`] = `

      + > + +
      `; diff --git a/web-ui/src/components/edit_skills/EditSkillsCard.test.jsx b/web-ui/src/components/edit_skills/EditSkillsCard.test.jsx index f5a2301ab4..84f732624e 100644 --- a/web-ui/src/components/edit_skills/EditSkillsCard.test.jsx +++ b/web-ui/src/components/edit_skills/EditSkillsCard.test.jsx @@ -35,7 +35,7 @@ const initialState = { guilds: [], teams: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0, memberProfiles: [ diff --git a/web-ui/src/components/guides/GuideLink.jsx b/web-ui/src/components/guides/GuideLink.jsx index 180af0da04..674cfc22bf 100644 --- a/web-ui/src/components/guides/GuideLink.jsx +++ b/web-ui/src/components/guides/GuideLink.jsx @@ -17,6 +17,7 @@ const propTypes = { const GuideLink = props => { return ( { +const GuidesPanel = () => { + const { state } = useContext(AppContext); + const csrf = selectCsrfToken(state); + const userRoles = selectCurrentUserRoles(state); + const allRoles = selectRoles(state); + + const [documents, setDocuments] = useState([]); + useEffect(() => { const getDocuments = async () => { - if (mockuments && mockuments.length > 0) { - setDocuments(mockuments); - } else { - const memberRoleId = allRoles.find(role => role.role === roleName)?.id; - const res = await getDocumentsForRoleId(memberRoleId, csrf); - const responseBody = res.payload?.data && !res.error - ? res.payload.data - : undefined; - setDocuments(responseBody?.length > 0 ? responseBody : fallback); + const docs = []; + if(userRoles) { + for (const roleName of userRoles) { + const memberRoleId = allRoles.find(role => role.role === roleName)?.id; + const res = await getDocumentsForRoleId(memberRoleId, csrf); + const responseBody = res.payload?.data && !res.error + ? res.payload.data + : undefined; + if (responseBody?.length > 0) { + docs.push(...responseBody); + } + } } + + setDocuments(docs.length > 0 ? docs : fallback); }; if (csrf) { getDocuments(); } - }, [csrf]); -}; + }, [allRoles, userRoles, csrf, setDocuments, getDocumentsForRoleId]); -export const generate = (title, documents) => { return ( - - } title={title} /> - - {documents.map(doc => ( - - ))} - - - ); -}; - -const GuidesPanel = () => { - const { state } = useContext(AppContext); - const csrf = selectCsrfToken(state); - - const [documents, setDocuments] = useState([]); - fetchDocumentsForRole('MEMBER', selectRoles(state), csrf, setDocuments, fallbackPdfs, state.mockuments); - - return generate('Team Member Resources', documents); + + } title={"Check-In Resources"} /> + + {documents.map(doc => ( + + ))} + + + );; }; export default GuidesPanel; diff --git a/web-ui/src/components/guides/GuidesPanel.test.jsx b/web-ui/src/components/guides/GuidesPanel.test.jsx index a525ad413b..5c4760b87d 100644 --- a/web-ui/src/components/guides/GuidesPanel.test.jsx +++ b/web-ui/src/components/guides/GuidesPanel.test.jsx @@ -2,13 +2,15 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import GuidesPanel from './GuidesPanel'; import { AppContextProvider } from '../../context/AppContext.jsx'; +import {setupServer} from "msw/node"; +import {http, HttpResponse} from "msw"; const initialState = { state: { csrf: 'O_3eLX2-e05qpS_yOeg1ZVAs9nDhspEi', userProfile: { name: 'holmes', - role: ['MEMBER'], + role: ['MEMBER', 'PDL'], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' }, @@ -16,26 +18,49 @@ const initialState = { { id: 1, role: 'MEMBER' }, { id: 2, role: 'PDL' } ], - mockuments: [ - { - id: 'mockument-1', - name: 'Expectations Discussion Guide for Team Members', - url: '/pdfs/Expectations_Discussion_Guide_for_Team_Members.pdf', - description: 'My description' - }, - { - id: 'mockument-2', - name: 'Expectations Worksheet', - url: '/pdfs/Expectations_Worksheet.pdf' - } - ], teams: [] } }; +const mockuments = [ + { + id: 'mockument-1', + name: 'Expectations Discussion Guide for Team Members', + url: '/pdfs/Expectations_Discussion_Guide_for_Team_Members.pdf', + description: 'My description' + }, + { + id: 'mockument-2', + name: 'Expectations Worksheet', + url: '/pdfs/Expectations_Worksheet.pdf', + description: 'My worksheet' + } +]; + +const pdlMockuments = [ + { + id: 'mockument-3', + name: 'Expectations Discussion Guide for PDLs', + url: '/pdfs/Expectations_Discussion_Guide_for_PDLs.pdf', + description: 'My PDL description' + } +]; + +const server = setupServer( + http.get('http://localhost:8080/services/document/1', () => { + return HttpResponse.json(mockuments); + }), + http.get('http://localhost:8080/services/document/2', () => { + return HttpResponse.json(pdlMockuments); + }) +); + +beforeAll(() => server.listen({ onUnhandledRequest(request, print) {} })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); -it('renders correctly', () => { - snapshot( +it('renders correctly', async () => { + await waitForSnapshot('mockument-3', diff --git a/web-ui/src/components/guides/PDLGuidesPanel.jsx b/web-ui/src/components/guides/PDLGuidesPanel.jsx deleted file mode 100644 index fb0a69e815..0000000000 --- a/web-ui/src/components/guides/PDLGuidesPanel.jsx +++ /dev/null @@ -1,41 +0,0 @@ -import { useContext, useState } from 'react'; - -import { AppContext } from '../../context/AppContext'; -import { selectCsrfToken, selectRoles, selectUserProfile } from '../../context/selectors.js'; - -import { fetchDocumentsForRole, generate } from './GuidesPanel.jsx'; -import './GuidesPanel.css'; - -const fallbackPdfs = [ - { - id: '1', - name: 'Development Discussion Guide for PDLs', - url: '/pdfs/Development_Discussion_Guide_for_PDLs.pdf' - }, - { - id: '2', - name: 'Expectations Discussion Guide for PDLs', - url: '/pdfs/Expectations_Discussion_Guide_for_PDLs.pdf' - }, - { - id: '3', - name: 'Feedback Discussion Guide for PDLs', - url: '/pdfs/Feedback_Discussion_Guide_for_PDLs.pdf' - } -]; - -const PDLGuidesPanel = () => { - const { state } = useContext(AppContext); - const csrf = selectCsrfToken(state); - const isPdl = selectUserProfile(state)?.role?.includes('PDL'); - const [documents, setDocuments] = useState(fallbackPdfs); - - if (isPdl) { - fetchDocumentsForRole('PDL', selectRoles(state), csrf, setDocuments, fallbackPdfs, state.mockuments); - return generate('Development Lead Guides', documents); - } else { - return null; - } -}; - -export default PDLGuidesPanel; diff --git a/web-ui/src/components/guides/PDLGuidesPanel.test.jsx b/web-ui/src/components/guides/PDLGuidesPanel.test.jsx deleted file mode 100644 index 51f38ba3d0..0000000000 --- a/web-ui/src/components/guides/PDLGuidesPanel.test.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; -import { AppContextProvider } from '../../context/AppContext'; -import PDLGuidesPanel from './PDLGuidesPanel'; -import { Router } from 'react-router-dom'; -import { createBrowserHistory } from 'history'; - -const initialState = { - state: { - csrf: 'O_3eLX2-e05qpS_yOeg1ZVAs9nDhspEi', - userProfile: { - name: 'holmes', - role: ['MEMBER'], - imageUrl: - 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' - }, - roles: [ - { id: 1, role: 'MEMBER' }, - { id: 2, role: 'PDL' } - ], - mockuments: [ - { - id: 'mockument-1', - name: 'Development Discussion Guide for PDLs', - url: '/pdfs/Development_Discussion_Guide_for_PDLs.pdf' - } - ], - teams: [] - } -}; - -const pdlState = { - state: { - ...initialState.state, - userProfile: { - ...initialState.state.userProfile, - role: ['MEMBER', 'PDL'] - } - } -}; - -it('renders correctly', () => { - const customHistory = createBrowserHistory(); - snapshot( - - - - - - ); -}); - -it('doesn\'t render for non-pdls', () => { - const customHistory = createBrowserHistory(); - snapshot( - - - - - - ); -}); diff --git a/web-ui/src/components/guides/__snapshots__/GuideLink.test.jsx.snap b/web-ui/src/components/guides/__snapshots__/GuideLink.test.jsx.snap index 6e6280cdc2..9d502125f1 100644 --- a/web-ui/src/components/guides/__snapshots__/GuideLink.test.jsx.snap +++ b/web-ui/src/components/guides/__snapshots__/GuideLink.test.jsx.snap @@ -4,6 +4,7 @@ exports[`renders correctly 1`] = ` @@ -38,6 +38,7 @@ exports[`renders correctly 1`] = ` > @@ -58,17 +59,44 @@ exports[`renders correctly 1`] = `
      Expectations Worksheet +

      + My worksheet +

      +
      +
      + +
      + + Expectations Discussion Guide for PDLs + +

      + My PDL description +

    diff --git a/web-ui/src/components/guild-results/EditGuildModal.spec.jsx b/web-ui/src/components/guild-results/EditGuildModal.spec.jsx index bbc3943f2d..9aeffb75f2 100644 --- a/web-ui/src/components/guild-results/EditGuildModal.spec.jsx +++ b/web-ui/src/components/guild-results/EditGuildModal.spec.jsx @@ -59,17 +59,17 @@ const initialState = { name: 'Current User', firstName: 'Current', lastName: 'User', + id: currentUserProfile.id, role: ['MEMBER'], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg', - memberProfile: currentUserProfile }, checkins: [], guilds: [testGuild, emptyGuild], teams: [], skills: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0, memberProfiles: [ diff --git a/web-ui/src/components/guild-results/GuildSummaryCard.jsx b/web-ui/src/components/guild-results/GuildSummaryCard.jsx index 5b59c657c0..8b1b7dc62a 100644 --- a/web-ui/src/components/guild-results/GuildSummaryCard.jsx +++ b/web-ui/src/components/guild-results/GuildSummaryCard.jsx @@ -6,6 +6,7 @@ import { styled } from '@mui/material/styles'; import { AppContext } from '../../context/AppContext'; import { UPDATE_GUILDS, UPDATE_TOAST } from '../../context/actions'; +import { selectIsAdmin, selectCurrentUser } from '../../context/selectors'; import EditGuildModal from './EditGuildModal'; import { @@ -57,7 +58,7 @@ const StyledCard = styled(Card)(() => ({ const inactiveStyle = { 'color': 'var(--action-disabled)', - 'font-size': '0.75em', + 'fontSize': '0.75em', }; const propTypes = { @@ -72,11 +73,11 @@ const displayName = 'GuildSummaryCard'; const GuildSummaryCard = ({ guild, index, isOpen, onGuildSelect }) => { const { state, dispatch } = useContext(AppContext); - const { guilds, userProfile, csrf } = state; + const { guilds, csrf } = state; const [open, setOpen] = useState(isOpen); const [tooltipIsOpen, setTooltipIsOpen] = useState(false); - const isAdmin = - userProfile && userProfile.role && userProfile.role.includes('ADMIN'); + const isAdmin = selectIsAdmin(state); + const currentUser = selectCurrentUser(state); let leads = guild.guildMembers == null @@ -90,7 +91,7 @@ const GuildSummaryCard = ({ guild, index, isOpen, onGuildSelect }) => { const isGuildLead = leads === null ? false - : leads.some(lead => lead.memberId === userProfile.memberProfile.id); + : leads.some(lead => lead.memberId === currentUser.id); const handleOpen = () => { setOpen(true); diff --git a/web-ui/src/components/guild-results/GuildSummaryCard.test.jsx b/web-ui/src/components/guild-results/GuildSummaryCard.test.jsx index bd7b5c6cec..c6f534b6d5 100644 --- a/web-ui/src/components/guild-results/GuildSummaryCard.test.jsx +++ b/web-ui/src/components/guild-results/GuildSummaryCard.test.jsx @@ -28,17 +28,18 @@ const initialState = { state: { userProfile: { name: 'holmes', - memberProfile: { - id: '3fa4-5717-4562-b3fc-2c963f66afa9', - pdlId: '', - title: 'Tester', - workEmail: 'test@tester.com' - }, + id: '3fa4-5717-4562-b3fc-2c963f66afa9', role: ['MEMBER'], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' }, - guilds + guilds, + memberProfiles: [{ + id: '3fa4-5717-4562-b3fc-2c963f66afa9', + pdlId: '', + title: 'Tester', + workEmail: 'test@tester.com' + }] } }; diff --git a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.jsx b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.jsx index a24fc85109..cca3030112 100644 --- a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.jsx +++ b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.jsx @@ -38,7 +38,7 @@ import { selectCsrfToken, selectCurrentMembers, selectActiveGuilds, - selectMappedUserRoles, + selectMappedMemberRoles, selectRoles, selectSkills, selectSubordinates, @@ -320,11 +320,11 @@ const MemberSelectorDialog = ({ ); break; case FilterType.ROLE: - const mappedUserRoles = selectMappedUserRoles(state); + const mappedMemberRoles = selectMappedMemberRoles(state); filteredMemberList = filteredMemberList.filter( member => - member.id in mappedUserRoles && - mappedUserRoles[member.id].has(filter.role) + member.id in mappedMemberRoles && + mappedMemberRoles[member.id].has(filter.role) ); break; case FilterType.SKILL: diff --git a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.spec.jsx b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.spec.jsx index c919b78945..4db49171b9 100644 --- a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.spec.jsx +++ b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.spec.jsx @@ -63,7 +63,7 @@ const initialState = { guilds: [testGuild], teams: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0 }; diff --git a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.test.jsx b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.test.jsx index 44caecfea8..7a012b78d7 100644 --- a/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.test.jsx +++ b/web-ui/src/components/member_selector/member_selector_dialog/MemberSelectorDialog.test.jsx @@ -50,7 +50,7 @@ const initialState = { guilds: [], teams: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0 } diff --git a/web-ui/src/components/menu/Menu.jsx b/web-ui/src/components/menu/Menu.jsx index 442ebe8fdd..fdcd21a184 100644 --- a/web-ui/src/components/menu/Menu.jsx +++ b/web-ui/src/components/menu/Menu.jsx @@ -28,6 +28,7 @@ import { selectCanEditAllOrganizationMembers, selectHasUploadHoursPermission, selectHasPermissionAssignmentPermission, + selectCurrentUser, } from '../../context/selectors'; import { UPDATE_TOAST } from '../../context/actions'; @@ -108,8 +109,7 @@ function Menu({ children }) { const { state, dispatch } = useContext(AppContext); const { userProfile } = state; const csrf = selectCsrfToken(state); - const { id, workEmail } = - userProfile && userProfile.memberProfile ? userProfile.memberProfile : {}; + const { id, workEmail } = selectCurrentUser(state); const hasReportPermission = selectHasReportPermission(state); const canViewFeedbackAnswer = selectCanViewFeedbackAnswerPermission(state); const canViewFeedbackRequest = selectCanViewFeedbackRequestPermission(state); diff --git a/web-ui/src/components/menu/Menu.test.jsx b/web-ui/src/components/menu/Menu.test.jsx index e3851f4cfc..d6faf02627 100644 --- a/web-ui/src/components/menu/Menu.test.jsx +++ b/web-ui/src/components/menu/Menu.test.jsx @@ -10,17 +10,20 @@ const initialState = { state: { userProfile: { name: 'holmes', - memberProfile: { - id: testId, - pdlId: '', - title: 'Tester', - workEmail: 'test@tester.com' - }, + id: testId, role: ['MEMBER'], permissions: [], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' - } + }, + memberProfiles: [ + { + id: testId, + pdlId: '', + title: 'Tester', + workEmail: 'test@tester.com' + } + ] } }; @@ -28,16 +31,20 @@ const adminState = { state: { userProfile: { name: 'holmes', - memberProfile: { - pdlId: '', - title: 'Tester', - workEmail: 'test@tester.com' - }, + id: testId, role: ['MEMBER', 'ADMIN'], permissions: [{ permission: 'CAN_VIEW_SKILLS_REPORT' }], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' - } + }, + memberProfiles: [ + { + id: testId, + pdlId: '', + title: 'Tester', + workEmail: 'test@tester.com' + } + ] } }; @@ -45,16 +52,20 @@ const pdlState = { state: { userProfile: { name: 'holmes', - memberProfile: { - pdlId: '', - title: 'Tester', - workEmail: 'test@tester.com' - }, + id: testId, role: ['MEMBER', 'PDL'], permissions: [], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' - } + }, + memberProfiles: [ + { + id: testId, + pdlId: '', + title: 'Tester', + workEmail: 'test@tester.com' + } + ] } }; diff --git a/web-ui/src/components/menu/__snapshots__/Menu.test.jsx.snap b/web-ui/src/components/menu/__snapshots__/Menu.test.jsx.snap index fdf4f92152..1f6edf6594 100644 --- a/web-ui/src/components/menu/__snapshots__/Menu.test.jsx.snap +++ b/web-ui/src/components/menu/__snapshots__/Menu.test.jsx.snap @@ -450,7 +450,7 @@ exports[` > renders correctly for admin 1`] = `
    > renders correctly for pdl 1`] = `
    { ) : ( { ) : ( `; +exports[`renders correctly 1`] = ` +
    +
    +
    +
    + +
    +
    +

    + Private Notes +

    +
    +
    +
    +
    +
    + + + + +
    +
    +
    +
    +
    +`; diff --git a/web-ui/src/components/reviews/periods/ReviewPeriods.jsx b/web-ui/src/components/reviews/periods/ReviewPeriods.jsx index cedd2d151e..14df1e06b1 100644 --- a/web-ui/src/components/reviews/periods/ReviewPeriods.jsx +++ b/web-ui/src/components/reviews/periods/ReviewPeriods.jsx @@ -32,8 +32,7 @@ import { selectCurrentUserId, selectHasCreateReviewPeriodPermission, selectReviewPeriod, - selectReviewPeriods, - selectUserProfile + selectReviewPeriods } from '../../../context/selectors'; import ReviewPeriodCard from './ReviewPeriodCard.jsx'; @@ -123,7 +122,6 @@ const ReviewPeriods = ({ onPeriodSelected, mode }) => { const currentUserId = selectCurrentUserId(state); const csrf = selectCsrfToken(state); - const userProfile = selectUserProfile(state); useEffect(() => { setPeriods(selectReviewPeriods(state) diff --git a/web-ui/src/components/select-skills-dialog/SelectSkillsDialog.test.jsx b/web-ui/src/components/select-skills-dialog/SelectSkillsDialog.test.jsx index d46627a86b..3c79d39c71 100644 --- a/web-ui/src/components/select-skills-dialog/SelectSkillsDialog.test.jsx +++ b/web-ui/src/components/select-skills-dialog/SelectSkillsDialog.test.jsx @@ -27,7 +27,7 @@ const initialState = { teams: [], skills: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0, memberProfiles: [] diff --git a/web-ui/src/components/team-member/TeamMemberContainer.jsx b/web-ui/src/components/team-member/TeamMemberContainer.jsx index ee9c607242..1447be65ee 100644 --- a/web-ui/src/components/team-member/TeamMemberContainer.jsx +++ b/web-ui/src/components/team-member/TeamMemberContainer.jsx @@ -3,16 +3,14 @@ import MemberIcon from './MemberIcon'; import { AppContext } from '../../context/AppContext'; import { getMembersByTeam, getTeamsByMember } from '../../api/team'; import { getMember } from '../../api/member'; +import { selectCurrentUser } from '../../context/selectors'; import './TeamMember.css'; const TeamMemberContainer = () => { const { state } = useContext(AppContext); - const { csrf, userProfile } = state; - const id = - userProfile && userProfile.memberProfile - ? userProfile.memberProfile.id - : undefined; + const { csrf } = state; + const { id } = selectCurrentUser(state); const [selectedProfile, setSelectedProfile] = useState({ name: null, imageUrl: null diff --git a/web-ui/src/components/team-results/EditTeamModal.spec.jsx b/web-ui/src/components/team-results/EditTeamModal.spec.jsx index c1ddefbd01..3f6b3f2f76 100644 --- a/web-ui/src/components/team-results/EditTeamModal.spec.jsx +++ b/web-ui/src/components/team-results/EditTeamModal.spec.jsx @@ -60,16 +60,16 @@ const initialState = { firstName: 'Current', lastName: 'User', role: ['MEMBER'], + id: currentUserProfile.id, imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg', - memberProfile: currentUserProfile }, checkins: [], guilds: [], teams: [testTeam, emptyTeam], skills: [], roles: [], - userRoles: [], + memberRoles: [], memberSkills: [], index: 0, memberProfiles: [ diff --git a/web-ui/src/components/team-results/TeamSummaryCard.jsx b/web-ui/src/components/team-results/TeamSummaryCard.jsx index 450390b4bc..9072b2c036 100644 --- a/web-ui/src/components/team-results/TeamSummaryCard.jsx +++ b/web-ui/src/components/team-results/TeamSummaryCard.jsx @@ -1,27 +1,15 @@ -import React, { useContext, useState, useCallback } from 'react'; -import { styled } from '@mui/material/styles'; -import { AppContext } from '../../context/AppContext'; -import { UPDATE_TEAMS, UPDATE_TOAST } from '../../context/actions'; +import React, {useContext, useState} from 'react'; +import {styled} from '@mui/material/styles'; +import {AppContext} from '../../context/AppContext'; +import {UPDATE_TEAMS} from '../../context/actions'; import EditTeamModal from './EditTeamModal'; import KudosDialog from '../kudos_dialog/KudosDialog'; -import { Link } from 'react-router-dom'; -import { - Button, - Card, - CardActions, - CardContent, - CardHeader, - Dialog, - DialogActions, - DialogContent, - DialogContentText, - DialogTitle, - Tooltip, - Typography, -} from '@mui/material'; +import {Link} from 'react-router-dom'; +import {Card, CardActions, CardContent, CardHeader, Tooltip, Typography,} from '@mui/material'; import PropTypes from 'prop-types'; -import { updateTeam } from '../../api/team.js'; +import {updateTeam} from '../../api/team.js'; import SplitButton from '../split-button/SplitButton'; +import {selectCurrentUser, selectIsAdmin} from "../../context/selectors.js"; const PREFIX = 'TeamSummaryCard'; const classes = { @@ -65,13 +53,13 @@ const displayName = 'TeamSummaryCard'; const TeamSummaryCard = ({ team, index, onTeamSelect, selectedTeamId }) => { const { state, dispatch } = useContext(AppContext); - const { teams, userProfile, csrf } = state; + const { teams, csrf } = state; const [openKudos, setOpenKudos] = useState(false); // const [selectedTeam, setSelectedTeam] = useState(null); const [tooltipIsOpen, setTooltipIsOpen] = useState(false); - const isAdmin = - userProfile && userProfile.role && userProfile.role.includes('ADMIN'); + const isAdmin = selectIsAdmin(state); + const currentUser = selectCurrentUser(state); let leads = team.teamMembers == null @@ -85,7 +73,7 @@ const TeamSummaryCard = ({ team, index, onTeamSelect, selectedTeamId }) => { const isTeamLead = leads === null ? false - : leads.some(lead => lead.memberId === userProfile.memberProfile.id); + : leads.some(lead => lead.memberId === currentUser.id); const handleOpenKudos = () => setOpenKudos(true); const handleCloseKudos = () => setOpenKudos(false); diff --git a/web-ui/src/components/team-results/TeamSummaryCard.test.jsx b/web-ui/src/components/team-results/TeamSummaryCard.test.jsx index 937367ce03..ac9eb6a7f9 100644 --- a/web-ui/src/components/team-results/TeamSummaryCard.test.jsx +++ b/web-ui/src/components/team-results/TeamSummaryCard.test.jsx @@ -27,17 +27,20 @@ const initialState = { state: { userProfile: { name: 'holmes', - memberProfile: { - id: '3fa4-5717-4562-b3fc-2c963f66afa9', - pdlId: '', - title: 'Tester', - workEmail: 'test@tester.com' - }, + id: '3fa4-5717-4562-b3fc-2c963f66afa9', role: ['MEMBER'], imageUrl: 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' }, - teams + teams, + memberProfiles: [ + { + id: '3fa4-5717-4562-b3fc-2c963f66afa9', + pdlId: '', + title: 'Tester', + workEmail: 'test@tester.com' + } + ] } }; diff --git a/web-ui/src/context/AppContext.jsx b/web-ui/src/context/AppContext.jsx index 78481c04e4..a8e8983d1c 100644 --- a/web-ui/src/context/AppContext.jsx +++ b/web-ui/src/context/AppContext.jsx @@ -1,11 +1,11 @@ import React, { useEffect, useReducer, useMemo } from 'react'; import { reducer, initialState } from './reducer'; +import { selectCurrentUser } from './selectors'; import { getCheckins, getAllCheckinsForAdmin } from './thunks'; import { MY_PROFILE_UPDATE, SET_CSRF, SET_ROLES, - SET_USER_ROLES, UPDATE_GUILDS, UPDATE_MEMBER_SKILLS, UPDATE_MEMBER_PROFILES, @@ -14,18 +14,19 @@ import { UPDATE_CERTIFICATIONS, UPDATE_TEAMS, UPDATE_PEOPLE_LOADING, - UPDATE_TEAMS_LOADING + UPDATE_TEAMS_LOADING, + SET_MEMBER_ROLES, } from './actions'; import { getCurrentUser, getAllMembers, - getAllTerminatedMembers + getAllTerminatedMembers, } from '../api/member'; import { selectCanViewCheckinsPermission, selectCanViewTerminatedMembers, } from './selectors'; -import { getAllRoles, getAllUserRoles } from '../api/roles'; +import { getAllRoles, getAllMemberRoles } from '../api/roles'; import { getMemberSkills } from '../api/memberskill'; import { BASE_API_URL } from '../api/api'; import { getAllGuilds } from '../api/guild'; @@ -53,13 +54,10 @@ const AppContextProvider = props => { ); const userProfile = state && state.userProfile ? state.userProfile : undefined; - const memberProfile = - userProfile && userProfile.memberProfile - ? userProfile.memberProfile - : undefined; - const id = memberProfile ? memberProfile.id : undefined; - const pdlId = memberProfile ? memberProfile.pdlId : undefined; + const id = userProfile ? userProfile.id : undefined; + const currentUser = selectCurrentUser(state); + const pdlId = currentUser ? currentUser.pdlId : undefined; const { csrf, guilds, @@ -70,7 +68,7 @@ const AppContextProvider = props => { skills, certifications, roles, - userRoles + memberRoles } = state; const url = `${BASE_API_URL}/csrf/cookie`; useEffect(() => { @@ -278,9 +276,9 @@ const AppContextProvider = props => { }, [csrf, roles]); useEffect(() => { - const getUserRoles = async () => { + const getMemberRoles = async () => { // make call to the API - let res = await getAllUserRoles(csrf); + let res = await getAllMemberRoles(csrf); return res.payload && res.payload.data && res.payload.status === 200 && @@ -289,12 +287,12 @@ const AppContextProvider = props => { : null; }; - if (csrf && !userRoles) { - getUserRoles().then(userRoles => { - dispatch({ type: SET_USER_ROLES, payload: userRoles }); + if (csrf && !memberRoles) { + getMemberRoles().then(memberRoles => { + dispatch({ type: SET_MEMBER_ROLES, payload: memberRoles }); }); } - }, [csrf, userRoles]); + }, [csrf, memberRoles]); const value = useMemo(() => { return { state, dispatch }; diff --git a/web-ui/src/context/actions.js b/web-ui/src/context/actions.js index 5e97a4b95a..554ebea917 100644 --- a/web-ui/src/context/actions.js +++ b/web-ui/src/context/actions.js @@ -11,7 +11,7 @@ export const DELETE_ROLE = '@@check-ins/delete-role'; export const MY_PROFILE_UPDATE = '@@check-ins/update_profile'; export const SET_CSRF = '@@check-ins/update_csrf'; export const SET_ROLES = '@@check-ins/set_roles'; -export const SET_USER_ROLES = '@@check-ins/set_user_roles'; +export const SET_MEMBER_ROLES = '@@check-ins/set_member_roles'; export const UPDATE_GUILD = '@@check-ins/update_guild'; export const UPDATE_GUILDS = '@@check-ins/update_guilds'; export const UPDATE_GUILD_MEMBERS = '@@check-ins/update_guild_members'; diff --git a/web-ui/src/context/reducer.js b/web-ui/src/context/reducer.js index 541d5184ae..a70c9bbcec 100644 --- a/web-ui/src/context/reducer.js +++ b/web-ui/src/context/reducer.js @@ -12,7 +12,7 @@ import { MY_PROFILE_UPDATE, SET_CSRF, SET_ROLES, - SET_USER_ROLES, + SET_MEMBER_ROLES, UPDATE_CHECKIN, UPDATE_CHECKINS, UPDATE_MEMBER_PROFILES, @@ -47,7 +47,7 @@ export const initialState = { terminatedMembers: [], memberSkills: null, roles: null, - userRoles: null, + memberRoles: null, skills: null, teams: null, guilds: null, @@ -81,12 +81,10 @@ const convertMemberDates = member => { export const reducer = (state, action) => { switch (action.type) { case MY_PROFILE_UPDATE: - convertMemberDates(action.payload.memberProfile); - state.userProfile = { ...action.payload }; + state.userProfile = { id: action.payload.memberProfile.id, role: action.payload.role, permissions: action.payload.permissions }; break; case UPDATE_CURRENT_USER_PROFILE: convertMemberDates(action.payload); - state.userProfile = { ...state.userProfile, memberProfile: { ...action.payload } }; const profileId = action.payload.id; const memberProfiles = state.memberProfiles.reduce((acc, current) => { if(current.id !== profileId) { @@ -174,7 +172,7 @@ export const reducer = (state, action) => { break; case UPDATE_MEMBER_PROFILES: action.payload.forEach(convertMemberDates); - const currentProfileId = state?.userProfile?.memberProfile?.id; + const currentProfileId = state?.userProfile?.id; const currentProfile = action.payload.find((current) => { if(currentProfileId && current.id === currentProfileId) { return current; @@ -215,8 +213,8 @@ export const reducer = (state, action) => { case SET_ROLES: state.roles = action.payload; break; - case SET_USER_ROLES: - state.userRoles = action.payload; + case SET_MEMBER_ROLES: + state.memberRoles = action.payload; break; case DELETE_ROLE: state.roles = state.roles.filter(role => role.id !== action.payload); diff --git a/web-ui/src/context/selectors.js b/web-ui/src/context/selectors.js index 27d53c1c10..5a039bb3e2 100644 --- a/web-ui/src/context/selectors.js +++ b/web-ui/src/context/selectors.js @@ -9,7 +9,7 @@ export const selectUserProfile = state => state.userProfile || {}; export const selectCheckins = state => state.checkins || []; export const selectCsrfToken = state => state.csrf; export const selectRoles = state => state.roles || []; -export const selectUserRoles = state => state.userRoles || []; +export const selectMemberRoles = state => state.memberRoles || []; export const selectTeams = state => state.teams || []; export const selectGuilds = state => state.guilds || []; export const selectLoading = state => state.loading; @@ -23,8 +23,8 @@ const hasPermission = permissionName => selectUserProfile, userProfile => userProfile && - userProfile.role && - userProfile.permissions?.some(p => p?.permission?.includes(permissionName)) + userProfile.permissions && + userProfile.permissions.some(p => p?.permission?.includes(permissionName)) ); export const selectTeamsLoading = createSelector(selectLoading, loading => { @@ -38,8 +38,9 @@ export const selectMemberProfilesLoading = createSelector( export const selectCurrentUser = createSelector( selectUserProfile, - userProfile => - userProfile && userProfile.memberProfile ? userProfile.memberProfile : {} + selectMemberProfiles, + (userProfile, memberProfiles) => + memberProfiles.find((current) => current?.id === userProfile?.id) || {} ); export const selectIsAdmin = createSelector( @@ -48,6 +49,11 @@ export const selectIsAdmin = createSelector( userProfile && userProfile.role && userProfile.role.includes('ADMIN') ); +export const selectCurrentUserRoles = createSelector( + selectUserProfile, + userProfile => userProfile.role || [] +); + export const selectHasPermissionAssignmentPermission = hasPermission( 'CAN_ASSIGN_ROLE_PERMISSIONS' ); @@ -258,7 +264,7 @@ export const selectIsPDL = createSelector( export const selectCurrentUserId = createSelector( selectCurrentUser, - profile => profile.id + profile => profile?.id ); export const selectOrderedSkills = createSelector(selectSkills, skills => @@ -362,12 +368,12 @@ export const selectPdlRoles = createSelector(selectRoles, roles => roles?.filter(role => role.role?.includes('PDL')) ); -export const selectTerminatedUserRoles = createSelector( - selectUserRoles, +export const selectTerminatedMemberRoles = createSelector( + selectMemberRoles, selectTerminatedMemberIds, - (userRoles, memberIds) => { - return userRoles?.filter(userRole => - memberIds.includes(userRole.memberRoleId.memberId) + (memberRoles, memberIds) => { + return memberRoles?.filter(memberRole => + memberIds.includes(memberRole.memberRoleId.memberId) ); } ); @@ -382,16 +388,16 @@ export const selectTerminatedMembersAsOfDate = createSelector( ); export const selectTerminatedMembersWithPDLRole = createSelector( - selectTerminatedUserRoles, + selectTerminatedMemberRoles, selectPdlRoles, selectProfileMapForTerminatedMembers, - (userRoles, pdlRoles, terminatedMembersProfileMap) => { - const terminatedPDLs = userRoles?.filter(userRole => - pdlRoles.find(role => role.id === userRole?.memberRoleId?.roleId) + (memberRoles, pdlRoles, terminatedMembersProfileMap) => { + const terminatedPDLs = memberRoles?.filter(memberRole => + pdlRoles.find(role => role.id === memberRole?.memberRoleId?.roleId) ); /** @type {MemberProfile[]} */ const terminatedMembersWithPDLRole = terminatedPDLs?.map( - userRole => terminatedMembersProfileMap[userRole?.memberRoleId?.memberId] + memberRole => terminatedMembersProfileMap[memberRole?.memberRoleId?.memberId] ); return terminatedMembersWithPDLRole; } @@ -409,46 +415,46 @@ export const selectTerminatedMembersAsOfDateWithPDLRole = createSelector( } ); -export const selectCurrentUserRoles = createSelector( - selectUserRoles, +export const selectActiveMemberRoles = createSelector( + selectMemberRoles, selectCurrentMemberIds, - (userRoles, memberIds) => - userRoles?.filter(userRole => - memberIds.includes(userRole.memberRoleId.memberId) + (memberRoles, memberIds) => + memberRoles?.filter(memberRole => + memberIds.includes(memberRole.memberRoleId.memberId) ) ); -export const selectMappedUserRoles = createSelector( - selectUserRoles, +export const selectMappedMemberRoles = createSelector( + selectMemberRoles, selectRoles, - (userRoles, roles) => { - const mappedUserRoles = {}; - userRoles.forEach(userRole => { - const memberId = userRole.memberRoleId.memberId; - const role = roles.find(role => role.id === userRole.memberRoleId.roleId); - if (!(memberId in mappedUserRoles)) { - mappedUserRoles[memberId] = new Set(); + (memberRoles, roles) => { + const mappedMemberRoles = {}; + memberRoles.forEach(memberRole => { + const memberId = memberRole.memberRoleId.memberId; + const role = roles.find(role => role.id === memberRole.memberRoleId.roleId); + if (!(memberId in mappedMemberRoles)) { + mappedMemberRoles[memberId] = new Set(); } - mappedUserRoles[memberId].add(role.role); + mappedMemberRoles[memberId].add(role.role); }); - return mappedUserRoles; + return mappedMemberRoles; } ); export const selectMappedPdls = createSelector( selectProfileMap, selectPdlRoles, - selectCurrentUserRoles, - (memberProfileMap, roles, userRoles) => - userRoles + selectActiveMemberRoles, + (memberProfileMap, roles, memberRoles) => + memberRoles ?.filter( - userRole => - roles.find(role => role.id === userRole?.memberRoleId?.roleId) !== + memberRole => + roles.find(role => role.id === memberRole?.memberRoleId?.roleId) !== undefined ) - ?.map(userRole => - userRole?.memberRoleId?.memberId in memberProfileMap - ? memberProfileMap[userRole?.memberRoleId?.memberId] + ?.map(memberRole => + memberRole?.memberRoleId?.memberId in memberProfileMap + ? memberProfileMap[memberRole?.memberRoleId?.memberId] : {} ) ); diff --git a/web-ui/src/context/selectors.test.js b/web-ui/src/context/selectors.test.js index 8f6007702e..e7362f8f4d 100644 --- a/web-ui/src/context/selectors.test.js +++ b/web-ui/src/context/selectors.test.js @@ -7,7 +7,7 @@ import { selectOrderedPdls, selectCheckinPDLS, selectTeamMembersWithCheckinPDL, - selectTerminatedUserRoles, + selectTerminatedMemberRoles, selectTerminatedMembersAsOfDate, selectTerminatedMembersWithPDLRole, selectTerminatedMembersAsOfDateWithPDLRole, @@ -24,6 +24,9 @@ import { selectActiveOrInactiveProfile, selectCanEditAllOrganizationMembers, selectCanViewTerminatedMembers, + selectHasAnniversaryReportPermission, + selectHasBirthdayReportPermission, + selectHasCheckinsReportPermission, selectHasSkillsReportPermission, selectHasTeamSkillsReportPermission, } from './selectors'; describe('Selectors', () => { @@ -348,7 +351,7 @@ describe('Selectors', () => { } ], roles: testRoles, - userRoles: testMemberRoles + memberRoles: testMemberRoles }; expect(selectMappedPdls(testState)).toEqual(matchingMembers); }); @@ -465,7 +468,7 @@ describe('Selectors', () => { } ], roles: testRoles, - userRoles: testMemberRoles + memberRoles: testMemberRoles }; expect(selectOrderedPdls(testState)).toEqual(matchingMembers); }); @@ -638,11 +641,11 @@ describe('Selectors', () => { ); }); - describe('selectTerminatedUserRoles', () => { - const mockSelectUserRoles = vi.fn(); + describe('selectTerminatedMemberRoles', () => { + const mockSelectMemberRoles = vi.fn(); const mockSelectTerminatedMemberIds = vi.fn(); it('should filter user roles by terminated member ids', () => { - const userRoles = [ + const memberRoles = [ { memberRoleId: { memberId: '6207b3fd-042d-49aa-9e28-dcc04f537c2d', @@ -657,10 +660,10 @@ describe('Selectors', () => { } ]; const memberIds = ['6207b3fd-042d-49aa-9e28-dcc04f537c2d']; - mockSelectUserRoles.mockReturnValue(userRoles); + mockSelectMemberRoles.mockReturnValue(memberRoles); mockSelectTerminatedMemberIds.mockReturnValue(memberIds); - const result = selectTerminatedUserRoles.resultFunc(userRoles, memberIds); + const result = selectTerminatedMemberRoles.resultFunc(memberRoles, memberIds); expect(result).toEqual([ { memberRoleId: { @@ -691,11 +694,11 @@ describe('Selectors', () => { }); describe('selectTerminatedMembersWithPDLRole', () => { - const mockSelectUserRoles = vi.fn(); + const mockSelectMemberRoles = vi.fn(); const mockSelectPdlRoles = vi.fn(); const mockSelectProfileMapForTerminatedMembers = vi.fn(); it('should filter terminated members with PDL role', () => { - const userRoles = [ + const memberRoles = [ { memberRoleId: { memberId: '6207b3fd-042d-49aa-9e28-dcc04f537c2d', @@ -720,14 +723,14 @@ describe('Selectors', () => { name: 'Jane Doe' } }; - mockSelectUserRoles.mockReturnValue(userRoles); + mockSelectMemberRoles.mockReturnValue(memberRoles); mockSelectPdlRoles.mockReturnValue(pdlRoles); mockSelectProfileMapForTerminatedMembers.mockReturnValue( terminatedMembersProfileMap ); const result = selectTerminatedMembersWithPDLRole.resultFunc( - userRoles, + memberRoles, pdlRoles, terminatedMembersProfileMap ); @@ -1325,7 +1328,7 @@ describe('Selectors', () => { const testState = { userProfile: { - memberProfile: testMemberProfiles[0] + id: testMemberProfiles[0].id, }, memberProfiles: testMemberProfiles }; @@ -1405,7 +1408,7 @@ describe('Selectors', () => { const testState = { userProfile: { - memberProfile: testMemberProfiles[0] + id: testMemberProfiles[0].id, }, memberProfiles: testMemberProfiles }; @@ -1420,10 +1423,9 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, - { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } + { permission: 'CAN_VIEW_FEEDBACK_ANSWER' }, ] } }; @@ -1431,20 +1433,53 @@ describe('Selectors', () => { expect(selectHasReportPermission(testState)).toBe(false); }); + it("selectHasReportPermission should return true when user has a 'REPORT' permission", () => { + const testState = { + userProfile: { + firstName: 'Huey', + lastName: 'Emmerich', + permissions: [ + { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, + { permission: 'CAN_VIEW_FEEDBACK_ANSWER' }, + { permission: 'CAN_VIEW_ANNIVERSARY_REPORT' }, + ] + } + }; + + expect(selectHasReportPermission(testState)).toBe(true); + }); + it("selectHasAnniversaryReportPermission should return false when user does not have 'CAN_VIEW_ANNIVERSARY_REPORT' permission", () => { + const testState2 = { + userProfile: { + firstName: 'Huey', + lastName: 'Emmerich', + permissions: [ + { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, + { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } + ] + } + }; + expect(selectHasAnniversaryReportPermission(testState2)).toBe(false); + }); + + it("selectHasAnniversaryReportPermission should return true when user has 'CAN_VIEW_ANNIVERSARY_REPORT' permission", () => { const testState1 = { userProfile: { firstName: 'Big', lastName: 'Boss', - role: 'ADMIN', permissions: [{ permission: 'CAN_VIEW_ANNIVERSARY_REPORT' }] } }; + + expect(selectHasAnniversaryReportPermission(testState1)).toBe(true); + }); + + it("selectHasBirthdayReportPermission should return false when user does not have 'CAN_VIEW_BIRTHDAY_REPORT' permission", () => { const testState2 = { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } @@ -1452,24 +1487,26 @@ describe('Selectors', () => { } }; - expect(selectHasReportPermission(testState1)).toBe(true); - expect(selectHasReportPermission(testState2)).toBe(false); + expect(selectHasBirthdayReportPermission(testState2)).toBe(false); }); - it("selectHasBirthdayReportPermission should return false when user does not have 'CAN_VIEW_BIRTHDAY_REPORT' permission", () => { + it("selectHasBirthdayReportPermission should return true when user has 'CAN_VIEW_BIRTHDAY_REPORT' permission", () => { const testState1 = { userProfile: { firstName: 'Big', lastName: 'Boss', - role: 'ADMIN', permissions: [{ permission: 'CAN_VIEW_BIRTHDAY_REPORT' }] } }; - const testState2 = { + + expect(selectHasBirthdayReportPermission(testState1)).toBe(true); + }); + + it("selectHasCheckinsReportPermission should return false when user does not have 'CAN_VIEW_CHECKINS_REPORT' permission", () => { + const testState = { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } @@ -1477,24 +1514,21 @@ describe('Selectors', () => { } }; - expect(selectHasReportPermission(testState1)).toBe(true); - expect(selectHasReportPermission(testState2)).toBe(false); + expect(selectHasCheckinsReportPermission(testState)).toBe(false); }); - it("selectHasCheckinsReportPermission should return false when user does not have 'CAN_VIEW_CHECKINS' permission", () => { + it("selectHasCheckinsReportPermission should return true when user has 'CAN_VIEW_CHECKINS_REPORT' permission", () => { const testState = { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ - { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, - { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } + { permission: 'CAN_VIEW_CHECKINS_REPORT' } ] } }; - expect(selectHasReportPermission(testState)).toBe(false); + expect(selectHasCheckinsReportPermission(testState)).toBe(true); }); it("selectHasSkillsReportPermission should return false when user does not have 'CAN_VIEW_SKILLS_REPORT' permission", () => { @@ -1502,7 +1536,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } @@ -1510,7 +1543,21 @@ describe('Selectors', () => { } }; - expect(selectHasReportPermission(testState)).toBe(false); + expect(selectHasSkillsReportPermission(testState)).toBe(false); + }); + + it("selectHasSkillsReportPermission should return true when user has 'CAN_VIEW_SKILLS_REPORT' permission", () => { + const testState = { + userProfile: { + firstName: 'Huey', + lastName: 'Emmerich', + permissions: [ + { permission: 'CAN_VIEW_SKILLS_REPORT' } + ] + } + }; + + expect(selectHasSkillsReportPermission(testState)).toBe(true); }); it("selectHasTeamSkillsReportPermission should return false when user does not have 'CAN_VIEW_SKILLS_REPORT' permission", () => { @@ -1518,7 +1565,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' } @@ -1526,7 +1572,21 @@ describe('Selectors', () => { } }; - expect(selectHasReportPermission(testState)).toBe(false); + expect(selectHasTeamSkillsReportPermission(testState)).toBe(false); + }); + + it("selectHasTeamSkillsReportPermission should return true when user has 'CAN_VIEW_SKILLS_REPORT' permission", () => { + const testState = { + userProfile: { + firstName: 'Huey', + lastName: 'Emmerich', + permissions: [ + { permission: 'CAN_VIEW_SKILLS_REPORT' } + ] + } + }; + + expect(selectHasTeamSkillsReportPermission(testState)).toBe(true); }); it("selectCanEditAllOrganizationMembers should return false when user does not have 'CAN_EDIT_ALL_ORGANIZATION_MEMBERS' permission", () => { @@ -1534,7 +1594,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' }, @@ -1550,7 +1609,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_EDIT_ALL_ORGANIZATION_MEMBERS' }, @@ -1567,7 +1625,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_FEEDBACK_ANSWER' }, @@ -1583,7 +1640,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_EDIT_ALL_ORGANIZATION_MEMBERS' }, @@ -1595,7 +1651,6 @@ describe('Selectors', () => { userProfile: { firstName: 'Huey', lastName: 'Emmerich', - role: 'MEMBER', permissions: [ { permission: 'CAN_VIEW_FEEDBACK_REQUEST' }, { permission: 'CAN_VIEW_TERMINATED_MEMBERS' }, diff --git a/web-ui/src/pages/CheckinsPage.jsx b/web-ui/src/pages/CheckinsPage.jsx index 822cb42ada..d9de8d11f6 100644 --- a/web-ui/src/pages/CheckinsPage.jsx +++ b/web-ui/src/pages/CheckinsPage.jsx @@ -24,7 +24,6 @@ import CheckinDocs from '../components/checkin/documents/CheckinDocs'; import CheckinsHistory from '../components/checkin/CheckinHistory'; import Profile from '../components/profile/Profile'; import GuidesPanel from '../components/guides/GuidesPanel'; -import PDLGuidesPanel from '../components/guides/PDLGuidesPanel'; import Note from '../components/notes/Note'; import PrivateNote from '../components/private-note/PrivateNote'; import Personnel from '../components/personnel/Personnel'; @@ -234,7 +233,6 @@ const CheckinsPage = () => {
    {isPdl && } -
    diff --git a/web-ui/src/pages/CheckinsPage.test.jsx b/web-ui/src/pages/CheckinsPage.test.jsx index 410fe78c60..16d05fac4a 100644 --- a/web-ui/src/pages/CheckinsPage.test.jsx +++ b/web-ui/src/pages/CheckinsPage.test.jsx @@ -1,9 +1,12 @@ import React from 'react'; import CheckinsPage from './CheckinsPage'; import { AppContextProvider } from '../context/AppContext'; -import { MemoryRouter } from 'react-router-dom'; +import { Router } from 'react-router-dom'; import { LocalizationProvider } from '@mui/x-date-pickers'; import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; +import {setupServer} from "msw/node"; +import {http, HttpResponse} from "msw"; +import {createMemoryHistory} from "history"; const mockMemberId = 'bf9975f8-a5b2-4551-b729-afd56b49e2cc'; const mockCheckinId = '3a1906df-d45c-4ff5-a6f8-7dacba97ff1a'; @@ -28,6 +31,7 @@ const initialState = { } ], userProfile: { + id: mockMemberId, name: 'holmes', role: ['MEMBER'], permissions: [{ permission: 'CAN_CREATE_CHECKINS' }], @@ -37,39 +41,70 @@ const initialState = { memberProfiles: [ { id: mockMemberId, - name: 'holmes' + name: 'holmes', + imageUrl: + 'https://upload.wikimedia.org/wikipedia/commons/7/74/SNL_MrBill_Doll.jpg' } ], roles: [ { id: 1, role: 'MEMBER' }, { id: 2, role: 'PDL' } ], - mockuments: [ - { - id: 'mockument-1', - name: 'Expectations Discussion Guide for Team Members', - url: '/pdfs/Expectations_Discussion_Guide_for_Team_Members.pdf', - description: 'My description' - }, - { - id: 'mockument-2', - name: 'Expectations Worksheet', - url: '/pdfs/Expectations_Worksheet.pdf' - } - ], teams: [], index: 0 } }; -it('renders correctly', () => { - snapshot( +const mockuments = [ + { + id: 'mockument-1', + name: 'Expectations Discussion Guide for Team Members', + url: '/pdfs/Expectations_Discussion_Guide_for_Team_Members.pdf', + description: 'My description' + }, + { + id: 'mockument-2', + name: 'Expectations Worksheet', + url: '/pdfs/Expectations_Worksheet.pdf', + description: 'My worksheet' + } +]; + +const server = setupServer( + http.get('http://localhost:8080/services/document/1', () => { + return HttpResponse.json(mockuments); + }) +); + +beforeAll(() => server.listen({ onUnhandledRequest(request, print) {} })); +afterEach(() => server.resetHandlers()); +afterAll(() => server.close()); + +const history = createMemoryHistory( + `/checkins/${mockMemberId}/${mockCheckinId}` +); + +vi.mock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), // use actual for all non-hook parts + useParams: () => ({ + memberId: mockMemberId, + checkinId: mockCheckinId + }), + useRouteMatch: () => ({ url: `/checkins/${mockMemberId}/${mockCheckinId}` }) +})); + +global.requestAnimationFrame = function (callback) { + setTimeout(callback, 0); +}; + +it('renders correctly', async () => { + await waitForSnapshot('mockument-2', - + - + ); }); diff --git a/web-ui/src/pages/MemberProfilePage.jsx b/web-ui/src/pages/MemberProfilePage.jsx index f5753fb230..c64401e033 100644 --- a/web-ui/src/pages/MemberProfilePage.jsx +++ b/web-ui/src/pages/MemberProfilePage.jsx @@ -82,7 +82,7 @@ const MemberProfilePage = () => { const [selectedMemberSkills, setSelectedMemberSkills] = useState([]); const [teams, setTeams] = useState([]); const [guilds, setGuilds] = useState([]); - const isCurrentUser = userProfile?.memberProfile?.id === memberId; + const isCurrentUser = userProfile?.id === memberId; useEffect(() => { async function getTeamsAndGuilds() { diff --git a/web-ui/src/pages/MemberProfilePage.test.jsx b/web-ui/src/pages/MemberProfilePage.test.jsx index 5fea6e21ad..9c26bd9d0a 100644 --- a/web-ui/src/pages/MemberProfilePage.test.jsx +++ b/web-ui/src/pages/MemberProfilePage.test.jsx @@ -11,10 +11,7 @@ const initialState = { { name: 'mr. test', firstName: 'mr', lastName: 'test', id: '1234' } ], userProfile: { - memberProfile: { - id: '1234', - name: 'mr. test' - } + id: '1234', } } }; diff --git a/web-ui/src/pages/ProfilePage.test.jsx b/web-ui/src/pages/ProfilePage.test.jsx index a5ef9e2caf..3ad03c06cc 100644 --- a/web-ui/src/pages/ProfilePage.test.jsx +++ b/web-ui/src/pages/ProfilePage.test.jsx @@ -7,18 +7,18 @@ const userProfile = { id: 'member-id', name: 'Mitch Hedberg', role: ['MEMBER'], - workEmail: 'hedbergm@objectcomputing.com', - title: 'Strategic Placement Specialist', - location: 'Roseville, Minnesota', - memberProfile: { - id: 'member-id', - bioText: 'Died too young.', - }, }; const userStateWithPermission = { state: { - memberProfiles: [ userProfile ], + memberProfiles: [{ + name: 'Mitch Hedberg', + id: 'member-id', + bioText: 'Died too young.', + workEmail: 'hedbergm@objectcomputing.com', + title: 'Strategic Placement Specialist', + location: 'Roseville, Minnesota', + }], userProfile: userProfile, } }; diff --git a/web-ui/src/pages/__snapshots__/CheckinsPage.test.jsx.snap b/web-ui/src/pages/__snapshots__/CheckinsPage.test.jsx.snap index ba49726a5f..69dd6ea19b 100644 --- a/web-ui/src/pages/__snapshots__/CheckinsPage.test.jsx.snap +++ b/web-ui/src/pages/__snapshots__/CheckinsPage.test.jsx.snap @@ -43,7 +43,9 @@ exports[`renders correctly 1`] = ` >

    + > + holmes +

    @@ -99,7 +101,7 @@ exports[`renders correctly 1`] = ` style="width: 18em;" >

    +
    +
    +
    + +
    +
    +

    + Agenda Items +

    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + +
    +
    +

    + Action Items +

    +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    + +
    +
    +

    + Notes for holmes +

    +
    +
    +
    +