Skip to content

Commit 9e8f76f

Browse files
authored
Merge pull request #1658 from topcoder-platform/pm-1506
feat(PM-1506): Overwrite role if the copilot is already a member of the project with "read"/"write" access
2 parents e950796 + 59cb7d7 commit 9e8f76f

File tree

4 files changed

+154
-104
lines changed

4 files changed

+154
-104
lines changed

src/components/Users/Users.module.scss

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@
4040
}
4141
}
4242

43+
.textContent {
44+
font-size: 18px;
45+
margin-top: 25px;
46+
}
47+
4348

4449
.row {
4550
display: flex;
@@ -400,7 +405,7 @@
400405
justify-content: center;
401406
}
402407

403-
.addButtonContainer {
408+
.addButtonContainer, .buttonWrapper {
404409
display: flex;
405410
justify-content: flex-start;
406411
height: 30px;
@@ -412,10 +417,6 @@
412417
}
413418
}
414419

415-
.addUserContentContainer {
416-
417-
}
418-
419420
.tcRadioButton {
420421
.tc-radioButton-label {
421422
@include roboto-light();

src/components/Users/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ class Users extends Component {
225225
addNewProjectMember={this.props.addNewProjectMember}
226226
onMemberInvited={this.props.addNewProjectInvite}
227227
onClose={this.resetAddUserState}
228+
projectOption={this.state.projectOption}
229+
projectMembers={projectMembers}
230+
updateProjectMember={updateProjectMember}
228231
/>
229232
)
230233
}

src/components/Users/user-add.modal.js

Lines changed: 142 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,30 @@ import Modal from '../Modal'
66
import SelectUserAutocomplete from '../SelectUserAutocomplete'
77
import { PROJECT_ROLES } from '../../config/constants'
88
import PrimaryButton from '../Buttons/PrimaryButton'
9-
import { addUserToProject, inviteUserToProject } from '../../services/projects'
9+
import { addUserToProject, inviteUserToProject, updateProjectMemberRole } from '../../services/projects'
1010

1111
import styles from './Users.module.scss'
1212

1313
const theme = {
1414
container: styles.modalContainer
1515
}
1616

17-
const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, onClose }) => {
17+
const UserAddModalContent = ({
18+
projectMembers,
19+
projectOption,
20+
projectId,
21+
addNewProjectMember,
22+
onMemberInvited,
23+
onClose,
24+
updateProjectMember
25+
}) => {
1826
const [userToAdd, setUserToAdd] = useState(null)
1927
const [userPermissionToAdd, setUserPermissionToAdd] = useState(PROJECT_ROLES.READ)
2028
const [showSelectUserError, setShowSelectUserError] = useState(false)
2129
const [addUserError, setAddUserError] = useState(null)
2230
const [isAdding, setIsAdding] = useState(false)
31+
const [isUserAddingFailed, setUserAddingFailed] = useState(false)
32+
const [existingRole, setExistingRole] = useState('')
2333

2434
const onUpdateUserToAdd = (option) => {
2535
if (option && option.value) {
@@ -52,8 +62,12 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited,
5262
})
5363
if (failed) {
5464
const error = get(failed, '0.message', 'User cannot be invited')
65+
const errorCode = get(failed, '0.error')
66+
const role = get(failed, '0.role')
5567
setAddUserError(error)
5668
setIsAdding(false)
69+
setUserAddingFailed(errorCode === 'ALREADY_MEMBER')
70+
setExistingRole(role)
5771
} else if (rest.message) {
5872
setAddUserError(rest.message)
5973
setIsAdding(false)
@@ -74,121 +88,152 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited,
7488
}
7589
}
7690

91+
const onConfirmCopilotRoleChange = async () => {
92+
const member = projectMembers.find(item => item.userId === userToAdd.userId)
93+
const action = member.role === 'manager' ? 'complete-copilot-requests' : ''
94+
const response = await updateProjectMemberRole(projectId, member.id, 'copilot', action)
95+
updateProjectMember(response)
96+
onClose()
97+
}
98+
99+
const onCancelCopilotRoleChange = () => {
100+
setUserAddingFailed(false)
101+
setAddUserError('')
102+
}
103+
77104
return (
78105
<Modal theme={theme} onCancel={onClose}>
79-
<div className={cn(styles.contentContainer, styles.confirm)}>
80-
<div className={styles.title}>Add User</div>
81-
<div className={styles.addUserContentContainer}>
82-
<div className={styles.row}>
83-
<div className={cn(styles.field, styles.col1, styles.addUserTitle)}>
84-
Member<span className={styles.required}>*</span> :
85-
</div>
86-
<div className={cn(styles.field, styles.col2)}>
87-
<SelectUserAutocomplete
88-
value={userToAdd ? { label: userToAdd.handle, value: userToAdd.userId.toString() } : null}
89-
onChange={onUpdateUserToAdd}
90-
/>
106+
{
107+
isUserAddingFailed && (['observer', 'customer', 'copilot', 'manager'].includes(existingRole)) && (
108+
<div className={cn(styles.contentContainer, styles.confirm)}>
109+
<div className={styles.textContent}>{`The copilot ${userToAdd.handle} is part of ${projectOption.label} project with ${existingRole} role.`}</div>
110+
<div className={styles.buttonWrapper}>
111+
<PrimaryButton onClick={onConfirmCopilotRoleChange} text={'Confirm'} type={'info'} />
112+
<PrimaryButton onClick={onCancelCopilotRoleChange} text={'Cancel'} type={'disabled'} />
91113
</div>
92114
</div>
93-
{showSelectUserError && (
94-
<div className={styles.row}>
95-
<div className={styles.errorMesssage}>Please select a member.</div>
96-
</div>
97-
)}
98-
<div className={styles.row}>
99-
<div className={cn(styles.field, styles.col1, styles.addUserTitle)}>
100-
<label htmlFor='memberToAdd'>Role :</label>
101-
</div>
102-
<div className={cn(styles.col5)}>
103-
<div className={styles.tcRadioButton}>
104-
<input
105-
name={`add-user-radio`}
106-
type='radio'
107-
id={`read-add-user`}
108-
checked={userPermissionToAdd === PROJECT_ROLES.READ}
109-
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.READ)}
110-
/>
111-
<label htmlFor={`read-add-user`}>
112-
<div>Read</div>
113-
<input type='hidden' />
114-
</label>
115+
)
116+
}
117+
{
118+
!isUserAddingFailed && (
119+
<div className={cn(styles.contentContainer, styles.confirm)}>
120+
<div className={styles.title}>Add User</div>
121+
<div className={styles.addUserContentContainer}>
122+
<div className={styles.row}>
123+
<div className={cn(styles.field, styles.col1, styles.addUserTitle)}>
124+
Member<span className={styles.required}>*</span> :
125+
</div>
126+
<div className={cn(styles.field, styles.col2)}>
127+
<SelectUserAutocomplete
128+
value={userToAdd ? { label: userToAdd.handle, value: userToAdd.userId.toString() } : null}
129+
onChange={onUpdateUserToAdd}
130+
/>
131+
</div>
115132
</div>
116-
</div>
117-
<div className={cn(styles.col5)}>
118-
<div className={styles.tcRadioButton}>
119-
<input
120-
name={`add-user-radio`}
121-
type='radio'
122-
id={`write-add-user`}
123-
checked={userPermissionToAdd === PROJECT_ROLES.WRITE}
124-
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.WRITE)}
125-
/>
126-
<label htmlFor={`write-add-user`}>
127-
<div>Write</div>
128-
<input type='hidden' />
129-
</label>
133+
{showSelectUserError && (
134+
<div className={styles.row}>
135+
<div className={styles.errorMesssage}>Please select a member.</div>
136+
</div>
137+
)}
138+
<div className={styles.row}>
139+
<div className={cn(styles.field, styles.col1, styles.addUserTitle)}>
140+
<label htmlFor='memberToAdd'>Role :</label>
141+
</div>
142+
<div className={cn(styles.col5)}>
143+
<div className={styles.tcRadioButton}>
144+
<input
145+
name={`add-user-radio`}
146+
type='radio'
147+
id={`read-add-user`}
148+
checked={userPermissionToAdd === PROJECT_ROLES.READ}
149+
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.READ)}
150+
/>
151+
<label htmlFor={`read-add-user`}>
152+
<div>Read</div>
153+
<input type='hidden' />
154+
</label>
155+
</div>
156+
</div>
157+
<div className={cn(styles.col5)}>
158+
<div className={styles.tcRadioButton}>
159+
<input
160+
name={`add-user-radio`}
161+
type='radio'
162+
id={`write-add-user`}
163+
checked={userPermissionToAdd === PROJECT_ROLES.WRITE}
164+
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.WRITE)}
165+
/>
166+
<label htmlFor={`write-add-user`}>
167+
<div>Write</div>
168+
<input type='hidden' />
169+
</label>
170+
</div>
171+
</div>
172+
<div className={cn(styles.col5)}>
173+
<div className={styles.tcRadioButton}>
174+
<input
175+
name={`add-user-radio`}
176+
type='radio'
177+
id={`full-access-add-user`}
178+
checked={userPermissionToAdd === PROJECT_ROLES.MANAGER}
179+
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.MANAGER)}
180+
/>
181+
<label htmlFor={`full-access-add-user`}>
182+
<div>Full Access</div>
183+
<input type='hidden' />
184+
</label>
185+
</div>
186+
</div>
187+
<div className={cn(styles.col5)}>
188+
<div className={styles.tcRadioButton}>
189+
<input
190+
name={`add-user-radio`}
191+
type='radio'
192+
id={`copilot-add-user`}
193+
checked={userPermissionToAdd === PROJECT_ROLES.COPILOT}
194+
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.COPILOT)}
195+
/>
196+
<label htmlFor={`copilot-add-user`}>
197+
<div>Copilot</div>
198+
<input type='hidden' />
199+
</label>
200+
</div>
201+
</div>
130202
</div>
203+
{addUserError && (
204+
<div className={styles.errorMesssage}>{addUserError}</div>
205+
)}
131206
</div>
132-
<div className={cn(styles.col5)}>
133-
<div className={styles.tcRadioButton}>
134-
<input
135-
name={`add-user-radio`}
136-
type='radio'
137-
id={`full-access-add-user`}
138-
checked={userPermissionToAdd === PROJECT_ROLES.MANAGER}
139-
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.MANAGER)}
207+
<div className={styles.buttonGroup}>
208+
<div className={styles.buttonSizeA}>
209+
<PrimaryButton
210+
text={'Close'}
211+
type={'info'}
212+
onClick={onClose}
140213
/>
141-
<label htmlFor={`full-access-add-user`}>
142-
<div>Full Access</div>
143-
<input type='hidden' />
144-
</label>
145214
</div>
146-
</div>
147-
<div className={cn(styles.col5)}>
148-
<div className={styles.tcRadioButton}>
149-
<input
150-
name={`add-user-radio`}
151-
type='radio'
152-
id={`copilot-add-user`}
153-
checked={userPermissionToAdd === PROJECT_ROLES.COPILOT}
154-
onChange={() => setUserPermissionToAdd(PROJECT_ROLES.COPILOT)}
215+
<div className={styles.buttonSizeA}>
216+
<PrimaryButton
217+
text={isAdding ? 'Adding user...' : 'Add User'}
218+
type={'info'}
219+
onClick={onAddUserConfirmClick}
155220
/>
156-
<label htmlFor={`copilot-add-user`}>
157-
<div>Copilot</div>
158-
<input type='hidden' />
159-
</label>
160221
</div>
161222
</div>
162223
</div>
163-
{addUserError && (
164-
<div className={styles.errorMesssage}>{addUserError}</div>
165-
)}
166-
</div>
167-
<div className={styles.buttonGroup}>
168-
<div className={styles.buttonSizeA}>
169-
<PrimaryButton
170-
text={'Close'}
171-
type={'info'}
172-
onClick={onClose}
173-
/>
174-
</div>
175-
<div className={styles.buttonSizeA}>
176-
<PrimaryButton
177-
text={isAdding ? 'Adding user...' : 'Add User'}
178-
type={'info'}
179-
onClick={onAddUserConfirmClick}
180-
/>
181-
</div>
182-
</div>
183-
</div>
224+
)
225+
}
184226
</Modal>
185227
)
186228
}
187229
UserAddModalContent.propTypes = {
188230
projectId: PropTypes.number.isRequired,
189231
addNewProjectMember: PropTypes.func.isRequired,
190232
onMemberInvited: PropTypes.func.isRequired,
191-
onClose: PropTypes.func.isRequired
233+
onClose: PropTypes.func.isRequired,
234+
projectOption: PropTypes.any.isRequired,
235+
projectMembers: PropTypes.array.isRequired,
236+
updateProjectMember: PropTypes.func.isRequired
192237
}
193238

194239
export default UserAddModalContent

src/services/projects.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,10 @@ export async function fetchProjectPhases (id) {
103103
* @param newRole the new role
104104
* @returns {Promise<*>}
105105
*/
106-
export async function updateProjectMemberRole (projectId, memberRecordId, newRole) {
106+
export async function updateProjectMemberRole (projectId, memberRecordId, newRole, action) {
107107
const response = await axiosInstance.patch(`${PROJECTS_API_URL}/${projectId}/members/${memberRecordId}`, {
108-
role: newRole
108+
role: newRole,
109+
action
109110
})
110111
return _.get(response, 'data')
111112
}

0 commit comments

Comments
 (0)