Skip to content

Commit 5823808

Browse files
committed
Merge remote-tracking branch 'origin/develop' into PM-973_invite-by-mail
2 parents 592b787 + 18768fd commit 5823808

File tree

11 files changed

+203
-29
lines changed

11 files changed

+203
-29
lines changed

src/actions/challenges.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ import {
5757
} from '../config/constants'
5858
import { loadProject } from './projects'
5959
import { removeChallengeFromPhaseProduct, saveChallengeAsPhaseProduct } from '../services/projects'
60-
import { checkAdmin } from '../util/tc'
60+
import { checkAdmin, checkManager } from '../util/tc'
6161

6262
/**
6363
* Member challenges related redux actions
@@ -159,7 +159,11 @@ export function loadChallengesByPage (
159159
filters['projectId'] = projectId
160160
} else if (_.isObject(projectId) && projectId.value > 0) {
161161
filters['projectId'] = projectId.value
162-
} else if (!checkAdmin(getState().auth.token) && userId) {
162+
} else if (
163+
!checkAdmin(getState().auth.token) &&
164+
!checkManager(getState().auth.token) &&
165+
userId
166+
) {
163167
// Note that we only add the memberId field if *no* project ID is given,
164168
// so that the list of *all challenges shows only those that the member is on
165169
filters['memberId'] = userId

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/actions/sidebar.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
UNLOAD_PROJECTS_SUCCESS,
1212
PROJECTS_PAGE_SIZE
1313
} from '../config/constants'
14-
import { checkAdmin } from '../util/tc'
14+
import { checkAdmin, checkManager } from '../util/tc'
1515
import _ from 'lodash'
1616

1717
/**
@@ -50,7 +50,7 @@ export function loadProjects (filterProjectName = '', paramFilters = {}) {
5050
}
5151
}
5252

53-
if (!checkAdmin(getState().auth.token)) {
53+
if (!checkAdmin(getState().auth.token) && !checkManager(getState().auth.token)) {
5454
filters['memberOnly'] = true
5555
}
5656

src/actions/users.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,55 @@ import {
1010
SEARCH_USER_PROJECTS_SUCCESS,
1111
SEARCH_USER_PROJECTS_FAILURE
1212
} from '../config/constants'
13+
import _ from 'lodash'
1314

1415
/**
1516
* Loads projects of the authenticated user
1617
*/
17-
export function loadAllUserProjects (isAdmin = true) {
18-
return (dispatch) => {
18+
export function loadAllUserProjects (params, isAdmin = true, isManager = true) {
19+
return (dispatch, getState) => {
1920
dispatch({
2021
type: LOAD_ALL_USER_PROJECTS_PENDING
2122
})
2223

24+
const state = getState().users
25+
2326
const filters = {
2427
status: 'active',
25-
sort: 'lastActivityAt desc'
28+
sort: 'lastActivityAt desc',
29+
perPage: 20,
30+
...params
2631
}
27-
if (!isAdmin) {
32+
33+
if (!isAdmin && !isManager) {
2834
filters['memberOnly'] = true
2935
}
3036

31-
fetchMemberProjects(filters).then(({ projects }) => dispatch({
37+
fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({
3238
type: LOAD_ALL_USER_PROJECTS_SUCCESS,
33-
projects
39+
projects: _.uniqBy((filters.page ? state.allUserProjects || [] : []).concat(projects), 'id'),
40+
total: pagination.xTotal,
41+
page: pagination.xPage
3442
})).catch(() => dispatch({
3543
type: LOAD_ALL_USER_PROJECTS_FAILURE
3644
}))
3745
}
3846
}
3947

48+
export function loadNextProjects (isAdmin = true, isManager = true) {
49+
return (dispatch, getState) => {
50+
const { page, total, allUserProjects } = getState().users
51+
if (allUserProjects.length >= total) {
52+
return
53+
}
54+
55+
loadAllUserProjects(_.assign({}, {
56+
perPage: 20,
57+
page: page + 1
58+
}), isAdmin, isManager)(dispatch, getState)
59+
}
60+
}
61+
4062
/**
4163
* Filter projects of the authenticated user
4264
*

src/components/Users/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Select from '../Select'
77
import UserCard from '../UserCard'
88
import PrimaryButton from '../Buttons/PrimaryButton'
99
import { PROJECT_ROLES, AUTOCOMPLETE_DEBOUNCE_TIME_MS } from '../../config/constants'
10-
import { checkAdmin } from '../../util/tc'
10+
import { checkAdmin, checkManager } from '../../util/tc'
1111
import { removeUserFromProject } from '../../services/projects'
1212
import { deleteProjectMemberInvite } from '../../services/projectMemberInvites'
1313
import ConfirmationModal from '../Modal/ConfirmationModal'
@@ -159,7 +159,8 @@ class Users extends Component {
159159
updateProjectNember,
160160
isEditable,
161161
isSearchingUserProjects,
162-
resultSearchUserProjects
162+
resultSearchUserProjects,
163+
loadNextProjects
163164
} = this.props
164165
const {
165166
searchKey
@@ -174,7 +175,8 @@ class Users extends Component {
174175
const membersExist = (projectMembers && projectMembers.length > 0) || (invitedMembers && invitedMembers.length > 0)
175176
const isCopilotOrManager = this.checkIsCopilotOrManager(projectMembers, loggedInHandle)
176177
const isAdmin = checkAdmin(this.props.auth.token)
177-
const showAddUser = isEditable && this.state.projectOption && (isCopilotOrManager || isAdmin)
178+
const isManager = checkManager(this.props.auth.token)
179+
const showAddUser = isEditable && this.state.projectOption && (isCopilotOrManager || isAdmin || isManager)
178180

179181
return (
180182
<div className={styles.contentContainer}>
@@ -192,6 +194,7 @@ class Users extends Component {
192194
onChange={(e) => { this.setProjectOption(e) }}
193195
onInputChange={this.debouncedOnInputChange}
194196
isLoading={isSearchingUserProjects}
197+
onMenuScrollBottom={loadNextProjects}
195198
filterOption={() => true}
196199
noOptionsMessage={() => isSearchingUserProjects ? 'Searching...' : 'No options'}
197200
/>
@@ -319,7 +322,8 @@ Users.propTypes = {
319322
projectMembers: PropTypes.arrayOf(PropTypes.object),
320323
invitedMembers: PropTypes.arrayOf(PropTypes.object),
321324
searchUserProjects: PropTypes.func.isRequired,
322-
resultSearchUserProjects: PropTypes.arrayOf(PropTypes.object)
325+
resultSearchUserProjects: PropTypes.arrayOf(PropTypes.object),
326+
loadNextProjects: PropTypes.func.isRequired
323327
}
324328

325329
export default Users

src/config/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@ export const COPILOT_ROLES = [
319319
'copilot'
320320
]
321321

322+
export const MANAGER_ROLES = [
323+
'project manager'
324+
]
325+
322326
export const downloadAttachmentURL = (challengeId, attachmentId, token) =>
323327
`${CHALLENGE_API_URL}/${challengeId}/attachments/${attachmentId}/download?token=${token}`
324328

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(false)
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
}

0 commit comments

Comments
 (0)