Skip to content

Commit 7f0cfd9

Browse files
authored
Merge pull request #1572 from topcoder-platform/PM-4650
PM-4650: show engagements tab for talent managers
2 parents 66a2afd + 707220e commit 7f0cfd9

5 files changed

Lines changed: 64 additions & 7 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getTabsConfig } from './tabs-config'
2+
3+
jest.mock('~/config', () => ({
4+
AppSubdomain: {
5+
work: 'work',
6+
},
7+
EnvironmentConfig: {
8+
SUBDOMAIN: 'work',
9+
},
10+
}), {
11+
virtual: true,
12+
})
13+
14+
jest.mock('../../../utils/permissions.utils', () => ({
15+
canViewAllEngagements: (userRoles: string[]) => (
16+
userRoles.includes('administrator') || userRoles.includes('talent manager')
17+
),
18+
}))
19+
20+
describe('getTabsConfig', () => {
21+
it('shows the engagements tab for talent managers on the common work page', () => {
22+
expect(getTabsConfig(['talent manager'], false)
23+
.map(tab => tab.id))
24+
.toContain('engagements')
25+
})
26+
27+
it('keeps the engagements tab hidden for project managers without talent manager access', () => {
28+
expect(getTabsConfig(['project manager'], false)
29+
.map(tab => tab.id))
30+
.not
31+
.toContain('engagements')
32+
})
33+
})

src/apps/work/src/lib/components/NavTabs/config/tabs-config.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
projectsRouteId,
1616
taasRouteId,
1717
} from '../../../../config/routes.config'
18+
import { canViewAllEngagements } from '../../../utils/permissions.utils'
1819

1920
function hasAnyRole(userRoles: string[], roles: string[]): boolean {
2021
return userRoles.some(role => roles.includes(role.toLowerCase()))
@@ -26,6 +27,7 @@ export function getTabsConfig(userRoles: string[], isAnonymous: boolean): TabsNa
2627
}
2728

2829
const isAdmin = hasAnyRole(userRoles, ADMIN_ROLES)
30+
const canViewEngagements = canViewAllEngagements(userRoles)
2931

3032
const tabs: TabsNavItem[] = [
3133
{
@@ -34,7 +36,7 @@ export function getTabsConfig(userRoles: string[], isAnonymous: boolean): TabsNa
3436
},
3537
]
3638

37-
if (isAdmin) {
39+
if (canViewEngagements) {
3840
tabs.push({
3941
id: engagementsRouteId,
4042
title: 'Engagements',

src/apps/work/src/lib/utils/permissions.utils.spec.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Project } from '../models'
44

55
import {
66
canCreateEngagement,
7+
canViewAllEngagements,
78
checkCanManageProject,
89
checkIsUserInvitedToProject,
910
checkProjectMembership,
@@ -78,6 +79,17 @@ describe('permissions.utils project management helpers', () => {
7879
.toBe(true)
7980
})
8081

82+
it('limits all-engagement access to admins and talent managers', () => {
83+
expect(canViewAllEngagements(['copilot']))
84+
.toBe(false)
85+
expect(canViewAllEngagements(['project manager']))
86+
.toBe(false)
87+
expect(canViewAllEngagements(['administrator']))
88+
.toBe(true)
89+
expect(canViewAllEngagements(['topcoder talent manager']))
90+
.toBe(true)
91+
})
92+
8193
it('normalizes project membership checks and role lookups by user id', () => {
8294
expect(checkProjectMembership(managedProject, '123'))
8395
.toBe(true)

src/apps/work/src/lib/utils/permissions.utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,19 @@ export function canEditTaasProject(userRoles: string[]): boolean {
161161
return hasAdminRole(userRoles) || hasCopilotRole(userRoles)
162162
}
163163

164+
/**
165+
* Returns whether the supplied user roles can access the common all-engagements view.
166+
*
167+
* This root work-app page is intended for admins and Talent Managers only.
168+
* Project managers still use project-scoped engagement pages.
169+
*
170+
* @param userRoles caller roles from the decoded auth token or app context.
171+
* @returns `true` when the caller can open the all-engagements page; otherwise `false`.
172+
*/
173+
export function canViewAllEngagements(userRoles: string[]): boolean {
174+
return hasAdminRole(userRoles) || checkTalentManager(userRoles)
175+
}
176+
164177
/**
165178
* Returns whether the supplied user roles can create engagements.
166179
*

src/apps/work/src/work-app.routes.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { WORK_MANAGER_ALLOWED_ROLES } from './config/access.config'
4242
import { ErrorMessage } from './lib/components'
4343
import { WorkAppContext } from './lib/contexts'
4444
import { WorkAppContextModel } from './lib/models'
45+
import { canViewAllEngagements } from './lib/utils'
4546

4647
const WorkApp: LazyLoadedComponent = lazyLoad(() => import('./WorkApp'))
4748

@@ -125,10 +126,6 @@ function canManageGroups(contextValue: WorkAppContextModel): boolean {
125126
return contextValue.isAdmin || contextValue.isCopilot || contextValue.isManager
126127
}
127128

128-
function canViewAllEngagements(contextValue: WorkAppContextModel): boolean {
129-
return contextValue.isAdmin
130-
}
131-
132129
const GroupsRouteGuard: FC<PropsWithChildren> = (props: PropsWithChildren) => {
133130
const contextValue: WorkAppContextModel = useContext(WorkAppContext)
134131

@@ -142,8 +139,8 @@ const GroupsRouteGuard: FC<PropsWithChildren> = (props: PropsWithChildren) => {
142139
const EngagementsRouteGuard: FC<PropsWithChildren> = (props: PropsWithChildren) => {
143140
const contextValue: WorkAppContextModel = useContext(WorkAppContext)
144141

145-
if (!canViewAllEngagements(contextValue)) {
146-
return <ErrorMessage message='You need Admin role to view all engagements.' />
142+
if (!canViewAllEngagements(contextValue.userRoles)) {
143+
return <ErrorMessage message='You need Admin or Talent Manager role to view all engagements.' />
147144
}
148145

149146
return <>{props.children}</>

0 commit comments

Comments
 (0)