Skip to content

Commit 90f0395

Browse files
authored
Merge pull request #1622 from topcoder-platform/pm-974_1
fix(PM-974) Allow project managers to view all projects
2 parents c7d59d7 + 592a0f3 commit 90f0395

File tree

5 files changed

+129
-8
lines changed

5 files changed

+129
-8
lines changed

src/actions/projects.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
fetchMemberProjects,
3333
updateProjectApi
3434
} from '../services/projects'
35-
import { checkAdmin } from '../util/tc'
35+
import { checkAdmin, checkManager } from '../util/tc'
3636

3737
function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) {
3838
return (dispatch, getState) => {
@@ -54,7 +54,7 @@ function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) {
5454
}
5555
}
5656

57-
if (!checkAdmin(getState().auth.token)) {
57+
if (!checkAdmin(getState().auth.token) && !checkManager(getState().auth.token)) {
5858
filters['memberOnly'] = true
5959
}
6060

src/config/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,10 @@ export const COPILOT_ROLES = [
308308
'copilot'
309309
]
310310

311+
export const MANAGER_ROLES = [
312+
'project manager'
313+
]
314+
311315
export const downloadAttachmentURL = (challengeId, attachmentId, token) =>
312316
`${CHALLENGE_API_URL}/${challengeId}/attachments/${attachmentId}/download?token=${token}`
313317

src/containers/Projects/index.js

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { withRouter, Link } from 'react-router-dom'
55
import { connect } from 'react-redux'
66
import PropTypes from 'prop-types'
77
import Loader from '../../components/Loader'
8-
import { checkAdminOrCopilot } from '../../util/tc'
8+
import { checkAdminOrCopilot, checkManager } from '../../util/tc'
99
import { PrimaryButton } from '../../components/Buttons'
1010
import Select from '../../components/Select'
1111
import ProjectCard from '../../components/ProjectCard'
@@ -18,11 +18,21 @@ import styles from './styles.module.scss'
1818
const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, loadMoreProjects, unloadProjects }) => {
1919
const [search, setSearch] = useState()
2020
const [projectStatus, setProjectStatus] = useState('')
21+
const [showOnlyMyProjects, setOnlyMyProjects] = useState(true)
2122
const selectedStatus = useMemo(() => PROJECT_STATUSES.find(s => s.value === projectStatus))
2223

24+
const isProjectManager = checkManager(auth.token)
2325
useEffect(() => {
24-
loadProjects(search, projectStatus ? { status: projectStatus } : {})
25-
}, [search, projectStatus])
26+
const params = {}
27+
if (projectStatus) {
28+
params.status = projectStatus
29+
}
30+
31+
if (isProjectManager) {
32+
params.memberOnly = showOnlyMyProjects
33+
}
34+
loadProjects(search, params)
35+
}, [search, projectStatus, showOnlyMyProjects, isProjectManager])
2636

2737
// unload projects on dismount
2838
useEffect(() => () => unloadProjects, [])
@@ -46,7 +56,7 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load
4656
)}
4757
</div>
4858
<div className={styles.searchWrapper}>
49-
<div className={styles['col-6']}>
59+
<div className={styles['col-4']}>
5060
<div className={cn(styles.field, styles.input1)}>
5161
<label>Search :</label>
5262
</div>
@@ -61,7 +71,7 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load
6171
/>
6272
</div>
6373
</div>
64-
<div className={styles['col-6']}>
74+
<div className={styles['col-4']}>
6575
<div className={cn(styles.field, styles.input1)}>
6676
<label>Project Status:</label>
6777
</div>
@@ -76,6 +86,25 @@ const Projects = ({ projects, auth, isLoading, projectsCount, loadProjects, load
7686
/>
7787
</div>
7888
</div>
89+
<div className={styles['col-4']}>
90+
{
91+
checkManager(auth.token) && (
92+
<div className={styles.tcCheckbox}>
93+
<input
94+
name='isOpenAdvanceSettings'
95+
type='checkbox'
96+
id='isOpenAdvanceSettings'
97+
checked={showOnlyMyProjects}
98+
onChange={() => setOnlyMyProjects(!showOnlyMyProjects)}
99+
/>
100+
<label htmlFor='isOpenAdvanceSettings'>
101+
<div>Only My Projects</div>
102+
<input type='hidden' />
103+
</label>
104+
</div>
105+
)
106+
}
107+
</div>
79108
</div>
80109
{projects.length > 0 ? (
81110
<>

src/containers/Projects/styles.module.scss

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
display: flex;
4444
gap: 10px;
4545
margin-bottom: 20px;
46+
align-items: end;
4647
.searchInput {
4748
width: 100%;
4849
height: 40px;
@@ -51,4 +52,85 @@
5152
border: 1px solid $light-gray;
5253
background-color: $lighter-gray;
5354
}
55+
56+
.tcCheckbox {
57+
@include tc-checkbox;
58+
59+
.tc-checkbox-label {
60+
@include roboto-light();
61+
62+
line-height: 17px;
63+
font-weight: 300;
64+
margin-left: 21px;
65+
user-select: none;
66+
cursor: pointer;
67+
width: 195px;
68+
font-size: 14px;
69+
color: #3d3d3d;
70+
}
71+
72+
height: 18px;
73+
width: 210px;
74+
margin: 0;
75+
padding: 0;
76+
vertical-align: bottom;
77+
position: relative;
78+
display: inline-block;
79+
margin-bottom: 4px;
80+
margin-left: 8px;
81+
82+
input[type=checkbox] {
83+
display: none;
84+
}
85+
86+
label {
87+
@include roboto-light();
88+
89+
line-height: 17px;
90+
font-weight: 300;
91+
cursor: pointer;
92+
position: absolute;
93+
display: inline-block;
94+
width: 18px;
95+
height: 18px;
96+
top: 0;
97+
left: 0;
98+
border: none;
99+
box-shadow: none;
100+
background: $tc-gray-30;
101+
transition: all 0.15s ease-in-out;
102+
103+
&::after {
104+
opacity: 0;
105+
content: '';
106+
position: absolute;
107+
width: 9px;
108+
height: 5px;
109+
background: transparent;
110+
top: 5px;
111+
left: 5px;
112+
border-top: none;
113+
border-right: none;
114+
transform: rotate(-45deg);
115+
transition: all 0.15s ease-in-out;
116+
}
117+
118+
&:hover::after {
119+
opacity: 0.3;
120+
}
121+
122+
div {
123+
margin-left: 24px;
124+
width: 300px;
125+
}
126+
}
127+
128+
input[type=checkbox]:checked ~ label {
129+
background: $tc-blue-20;
130+
}
131+
132+
input[type=checkbox]:checked + label::after {
133+
border-color: $white;
134+
}
135+
}
54136
}

src/util/tc.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
SUBMITTER_ROLE_UUID,
1111
READ_ONLY_ROLES,
1212
ALLOWED_DOWNLOAD_SUBMISSIONS_ROLES,
13-
ALLOWED_EDIT_RESOURCE_ROLES
13+
ALLOWED_EDIT_RESOURCE_ROLES,
14+
MANAGER_ROLES
1415
} from '../config/constants'
1516
import _ from 'lodash'
1617
import { decodeToken } from 'tc-auth-lib'
@@ -200,6 +201,11 @@ export const checkAdmin = (token) => {
200201
return roles.some(val => ADMIN_ROLES.indexOf(val.toLowerCase()) > -1)
201202
}
202203

204+
export const checkManager = (token) => {
205+
const tokenData = decodeToken(token)
206+
const roles = _.get(tokenData, 'roles')
207+
return roles.some(val => MANAGER_ROLES.indexOf(val.toLowerCase()) > -1)
208+
}
203209
/**
204210
* Checks if token has any of the copilot roles
205211
* @param token

0 commit comments

Comments
 (0)