Skip to content

Commit bbe6312

Browse files
authored
Merge pull request #1638 from topcoder-platform/pm-972
feat(PM-972): copilot invitation with email
2 parents 7d0fcd9 + 3b7fbe2 commit bbe6312

File tree

8 files changed

+56
-16
lines changed

8 files changed

+56
-16
lines changed

config/constants/development.js

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module.exports = {
2222
PROJECT_API_URL: `${DEV_API_HOSTNAME}/v5/projects`,
2323
GROUPS_API_URL: `${DEV_API_HOSTNAME}/v5/groups`,
2424
TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`,
25+
MEMBERS_API_URL: `${DEV_API_HOSTNAME}/v5/members`,
2526
RESOURCES_API_URL: `${DEV_API_HOSTNAME}/v5/resources`,
2627
RESOURCE_ROLES_API_URL: `${DEV_API_HOSTNAME}/v5/resource-roles`,
2728
SUBMISSIONS_API_URL: `${DEV_API_HOSTNAME}/v5/submissions`,

config/constants/production.js

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ module.exports = {
2121
PROJECT_API_URL: `${PROD_API_HOSTNAME}/v5/projects`,
2222
GROUPS_API_URL: `${PROD_API_HOSTNAME}/v5/groups`,
2323
TERMS_API_URL: `${PROD_API_HOSTNAME}/v5/terms`,
24+
MEMBERS_API_URL: `${PROD_API_HOSTNAME}/v5/members`,
2425
RESOURCES_API_URL: `${PROD_API_HOSTNAME}/v5/resources`,
2526
RESOURCE_ROLES_API_URL: `${PROD_API_HOSTNAME}/v5/resource-roles`,
2627
SUBMISSIONS_API_URL: `${PROD_API_HOSTNAME}/v5/submissions`,

src/components/Users/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class Users extends Component {
221221
<UserAddModalContent
222222
projectId={this.state.projectOption.value}
223223
addNewProjectMember={this.props.addNewProjectMember}
224+
onMemberInvited={this.props.addNewProjectInvite}
224225
onClose={this.resetAddUserState}
225226
/>
226227
)

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ const InviteUserModalContent = ({ projectId, onClose, onMemberInvited, projectMe
5555

5656
try {
5757
// api restriction: ONLY "customer" role can be invited via email
58-
const { success: invitations = [], failed } = await inviteUserToProject(projectId, emailToInvite, PROJECT_ROLES.CUSTOMER)
58+
const { success: invitations = [], failed } = await inviteUserToProject(projectId, {
59+
emails: [emailToInvite],
60+
role: PROJECT_ROLES.CUSTOMER
61+
})
5962

6063
if (failed) {
6164
const error = get(failed, '0.message', 'Unable to invite user')

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

+22-6
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ 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 } from '../../services/projects'
9+
import { addUserToProject, inviteUserToProject } 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, onClose }) => {
17+
const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, onClose }) => {
1818
const [userToAdd, setUserToAdd] = useState(null)
1919
const [userPermissionToAdd, setUserPermissionToAdd] = useState(PROJECT_ROLES.READ)
2020
const [showSelectUserError, setShowSelectUserError] = useState(false)
@@ -45,10 +45,25 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onClose }) => {
4545
setAddUserError(null)
4646

4747
try {
48-
const newUserInfo = await addUserToProject(projectId, userToAdd.userId, userPermissionToAdd)
49-
newUserInfo.handle = userToAdd.handle
50-
addNewProjectMember(newUserInfo)
51-
onClose()
48+
if (userPermissionToAdd === PROJECT_ROLES.COPILOT) {
49+
const { success: invitations = [], failed } = await inviteUserToProject(projectId, {
50+
handles: [userToAdd.handle],
51+
role: userPermissionToAdd
52+
})
53+
if (failed) {
54+
const error = get(failed, '0.message', 'Unable to invite user')
55+
setAddUserError(error)
56+
setIsAdding(false)
57+
} else {
58+
onMemberInvited(invitations[0] || {})
59+
onClose()
60+
}
61+
} else {
62+
const newUserInfo = await addUserToProject(projectId, userToAdd.userId, userPermissionToAdd)
63+
newUserInfo.handle = userToAdd.handle
64+
addNewProjectMember(newUserInfo)
65+
onClose()
66+
}
5267
} catch (e) {
5368
const error = get(e, 'response.data.message', 'Unable to add user')
5469
setAddUserError(error)
@@ -169,6 +184,7 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onClose }) => {
169184
UserAddModalContent.propTypes = {
170185
projectId: PropTypes.number.isRequired,
171186
addNewProjectMember: PropTypes.func.isRequired,
187+
onMemberInvited: PropTypes.func.isRequired,
172188
onClose: PropTypes.func.isRequired
173189
}
174190

src/config/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export const FILE_PICKER_PROGRESS_INTERVAL = 100
5959
export const FILE_PICKER_UPLOAD_RETRY = 2
6060
export const FILE_PICKER_UPLOAD_TIMEOUT = 30 * 60 * 1000 // 30 minutes
6161
export const SPECIFICATION_ATTACHMENTS_FOLDER = 'SPECIFICATION_ATTACHMENTS'
62+
export const MEMBERS_API_URL = process.env.MEMBERS_API_URL
6263

6364
export const getAWSContainerFileURL = (key) => `https://${FILE_PICKER_CONTAINER_NAME}.s3.amazonaws.com/${key}`
6465

src/containers/Users/index.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import _ from 'lodash'
44
import PT from 'prop-types'
55
import UsersComponent from '../../components/Users'
66
import { PROJECT_ROLES } from '../../config/constants'
7-
import { fetchProjectById } from '../../services/projects'
7+
import { fetchInviteMembers, fetchProjectById } from '../../services/projects'
88
import { checkAdmin, checkManager } from '../../util/tc'
99

1010
import {
@@ -80,12 +80,18 @@ class Users extends Component {
8080
}
8181

8282
loadProject (projectId) {
83-
fetchProjectById(projectId).then((project) => {
83+
fetchProjectById(projectId).then(async (project) => {
8484
const projectMembers = _.get(project, 'members')
8585
const invitedMembers = _.get(project, 'invites')
86+
const invitedUserIds = _.filter(_.map(invitedMembers, 'userId'))
87+
const invitedUsers = await fetchInviteMembers(invitedUserIds)
88+
8689
this.setState({
8790
projectMembers,
88-
invitedMembers
91+
invitedMembers: invitedMembers.map(m => ({
92+
...m,
93+
email: m.email || invitedUsers[m.userId].email
94+
}))
8995
})
9096
const { loggedInUser } = this.props
9197
this.updateLoginUserRoleInProject(projectMembers, loggedInUser)

src/services/projects.js

+17-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
GENERIC_PROJECT_MILESTONE_PRODUCT_TYPE,
99
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
1010
PHASE_PRODUCT_TEMPLATE_ID,
11-
PROJECTS_API_URL
11+
PROJECTS_API_URL,
12+
MEMBERS_API_URL
1213
} from '../config/constants'
1314
import { paginationHeaders } from '../util/pagination'
1415
import { createProjectMemberInvite } from './projectMemberInvites'
@@ -68,6 +69,19 @@ export async function fetchProjectById (id) {
6869
return _.get(response, 'data')
6970
}
7071

72+
/**
73+
* This fetches the user corresponding to the given userIds
74+
* @param {*} userIds
75+
*/
76+
export async function fetchInviteMembers (userIds) {
77+
const url = `${MEMBERS_API_URL}?${userIds.map(id => `userIds[]=${id}`).join('&')}`
78+
const { data = [] } = await axiosInstance.get(url)
79+
return data.reduce((acc, member) => {
80+
acc[member.userId] = member
81+
return acc
82+
}, {})
83+
}
84+
7185
/**
7286
* Api request for fetching project phases
7387
* @param id Project id
@@ -118,11 +132,8 @@ export async function addUserToProject (projectId, userId, role) {
118132
* @param role
119133
* @returns {Promise<*>}
120134
*/
121-
export async function inviteUserToProject (projectId, email, role) {
122-
return createProjectMemberInvite(projectId, {
123-
emails: [email],
124-
role: role
125-
})
135+
export async function inviteUserToProject (projectId, params) {
136+
return createProjectMemberInvite(projectId, params)
126137
}
127138

128139
/**

0 commit comments

Comments
 (0)