diff --git a/config/constants/development.js b/config/constants/development.js
index 311336ed..58e34497 100644
--- a/config/constants/development.js
+++ b/config/constants/development.js
@@ -22,6 +22,7 @@ module.exports = {
PROJECT_API_URL: `${DEV_API_HOSTNAME}/v5/projects`,
GROUPS_API_URL: `${DEV_API_HOSTNAME}/v5/groups`,
TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`,
+ MEMBERS_API_URL: `${DEV_API_HOSTNAME}/v5/members`,
RESOURCES_API_URL: `${DEV_API_HOSTNAME}/v5/resources`,
RESOURCE_ROLES_API_URL: `${DEV_API_HOSTNAME}/v5/resource-roles`,
SUBMISSIONS_API_URL: `${DEV_API_HOSTNAME}/v5/submissions`,
diff --git a/config/constants/production.js b/config/constants/production.js
index ff837ee2..10a13349 100644
--- a/config/constants/production.js
+++ b/config/constants/production.js
@@ -21,6 +21,7 @@ module.exports = {
PROJECT_API_URL: `${PROD_API_HOSTNAME}/v5/projects`,
GROUPS_API_URL: `${PROD_API_HOSTNAME}/v5/groups`,
TERMS_API_URL: `${PROD_API_HOSTNAME}/v5/terms`,
+ MEMBERS_API_URL: `${PROD_API_HOSTNAME}/v5/members`,
RESOURCES_API_URL: `${PROD_API_HOSTNAME}/v5/resources`,
RESOURCE_ROLES_API_URL: `${PROD_API_HOSTNAME}/v5/resource-roles`,
SUBMISSIONS_API_URL: `${PROD_API_HOSTNAME}/v5/submissions`,
diff --git a/src/components/UserCard/index.js b/src/components/UserCard/index.js
index 67f1cc08..ef79ce82 100644
--- a/src/components/UserCard/index.js
+++ b/src/components/UserCard/index.js
@@ -91,7 +91,7 @@ class UserCard extends Component {
)}
- {isInvite ? user.email : user.handle}
+ {isInvite ? (user.email || user.handle) : user.handle}
{!isInvite && (
<>
diff --git a/src/components/Users/index.js b/src/components/Users/index.js
index e328e230..3d00edc0 100644
--- a/src/components/Users/index.js
+++ b/src/components/Users/index.js
@@ -221,6 +221,7 @@ class Users extends Component {
)
diff --git a/src/components/Users/invite-user.modal.js b/src/components/Users/invite-user.modal.js
index 41b1dff4..37caa3b7 100644
--- a/src/components/Users/invite-user.modal.js
+++ b/src/components/Users/invite-user.modal.js
@@ -55,7 +55,10 @@ const InviteUserModalContent = ({ projectId, onClose, onMemberInvited, projectMe
try {
// api restriction: ONLY "customer" role can be invited via email
- const { success: invitations = [], failed } = await inviteUserToProject(projectId, emailToInvite, PROJECT_ROLES.CUSTOMER)
+ const { success: invitations = [], failed } = await inviteUserToProject(projectId, {
+ emails: [emailToInvite],
+ role: PROJECT_ROLES.CUSTOMER
+ })
if (failed) {
const error = get(failed, '0.message', 'Unable to invite user')
diff --git a/src/components/Users/user-add.modal.js b/src/components/Users/user-add.modal.js
index 79f08f93..58b68d20 100644
--- a/src/components/Users/user-add.modal.js
+++ b/src/components/Users/user-add.modal.js
@@ -6,7 +6,7 @@ import Modal from '../Modal'
import SelectUserAutocomplete from '../SelectUserAutocomplete'
import { PROJECT_ROLES } from '../../config/constants'
import PrimaryButton from '../Buttons/PrimaryButton'
-import { addUserToProject } from '../../services/projects'
+import { addUserToProject, inviteUserToProject } from '../../services/projects'
import styles from './Users.module.scss'
@@ -14,7 +14,7 @@ const theme = {
container: styles.modalContainer
}
-const UserAddModalContent = ({ projectId, addNewProjectMember, onClose }) => {
+const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, onClose }) => {
const [userToAdd, setUserToAdd] = useState(null)
const [userPermissionToAdd, setUserPermissionToAdd] = useState(PROJECT_ROLES.READ)
const [showSelectUserError, setShowSelectUserError] = useState(false)
@@ -45,10 +45,28 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onClose }) => {
setAddUserError(null)
try {
- const newUserInfo = await addUserToProject(projectId, userToAdd.userId, userPermissionToAdd)
- newUserInfo.handle = userToAdd.handle
- addNewProjectMember(newUserInfo)
- onClose()
+ if (userPermissionToAdd === PROJECT_ROLES.COPILOT) {
+ const { success: invitations = [], failed, ...rest } = await inviteUserToProject(projectId, {
+ handles: [userToAdd.handle],
+ role: userPermissionToAdd
+ })
+ if (failed) {
+ const error = get(failed, '0.message', 'User cannot be invited')
+ setAddUserError(error)
+ setIsAdding(false)
+ } else if (rest.message) {
+ setAddUserError(rest.message)
+ setIsAdding(false)
+ } else {
+ onMemberInvited(invitations[0] || {})
+ onClose()
+ }
+ } else {
+ const newUserInfo = await addUserToProject(projectId, userToAdd.userId, userPermissionToAdd)
+ newUserInfo.handle = userToAdd.handle
+ addNewProjectMember(newUserInfo)
+ onClose()
+ }
} catch (e) {
const error = get(e, 'response.data.message', 'Unable to add user')
setAddUserError(error)
@@ -169,6 +187,7 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onClose }) => {
UserAddModalContent.propTypes = {
projectId: PropTypes.number.isRequired,
addNewProjectMember: PropTypes.func.isRequired,
+ onMemberInvited: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired
}
diff --git a/src/config/constants.js b/src/config/constants.js
index 8729c02c..d40ddcbb 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -59,6 +59,7 @@ export const FILE_PICKER_PROGRESS_INTERVAL = 100
export const FILE_PICKER_UPLOAD_RETRY = 2
export const FILE_PICKER_UPLOAD_TIMEOUT = 30 * 60 * 1000 // 30 minutes
export const SPECIFICATION_ATTACHMENTS_FOLDER = 'SPECIFICATION_ATTACHMENTS'
+export const MEMBERS_API_URL = process.env.MEMBERS_API_URL
export const getAWSContainerFileURL = (key) => `https://${FILE_PICKER_CONTAINER_NAME}.s3.amazonaws.com/${key}`
diff --git a/src/containers/Users/index.js b/src/containers/Users/index.js
index dabaa0b5..08ee1f01 100644
--- a/src/containers/Users/index.js
+++ b/src/containers/Users/index.js
@@ -4,7 +4,7 @@ import _ from 'lodash'
import PT from 'prop-types'
import UsersComponent from '../../components/Users'
import { PROJECT_ROLES } from '../../config/constants'
-import { fetchProjectById } from '../../services/projects'
+import { fetchInviteMembers, fetchProjectById } from '../../services/projects'
import { checkAdmin, checkManager } from '../../util/tc'
import {
@@ -80,12 +80,18 @@ class Users extends Component {
}
loadProject (projectId) {
- fetchProjectById(projectId).then((project) => {
+ fetchProjectById(projectId).then(async (project) => {
const projectMembers = _.get(project, 'members')
const invitedMembers = _.get(project, 'invites')
+ const invitedUserIds = _.filter(_.map(invitedMembers, 'userId'))
+ const invitedUsers = await fetchInviteMembers(invitedUserIds)
+
this.setState({
projectMembers,
- invitedMembers
+ invitedMembers: invitedMembers.map(m => ({
+ ...m,
+ email: m.email || invitedUsers[m.userId].handle
+ }))
})
const { loggedInUser } = this.props
this.updateLoginUserRoleInProject(projectMembers, loggedInUser)
diff --git a/src/services/projects.js b/src/services/projects.js
index e586a144..a3cf43c0 100644
--- a/src/services/projects.js
+++ b/src/services/projects.js
@@ -8,7 +8,8 @@ import {
GENERIC_PROJECT_MILESTONE_PRODUCT_TYPE,
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
PHASE_PRODUCT_TEMPLATE_ID,
- PROJECTS_API_URL
+ PROJECTS_API_URL,
+ MEMBERS_API_URL
} from '../config/constants'
import { paginationHeaders } from '../util/pagination'
import { createProjectMemberInvite } from './projectMemberInvites'
@@ -68,6 +69,19 @@ export async function fetchProjectById (id) {
return _.get(response, 'data')
}
+/**
+ * This fetches the user corresponding to the given userIds
+ * @param {*} userIds
+ */
+export async function fetchInviteMembers (userIds) {
+ const url = `${MEMBERS_API_URL}?${userIds.map(id => `userIds[]=${id}`).join('&')}`
+ const { data = [] } = await axiosInstance.get(url)
+ return data.reduce((acc, member) => {
+ acc[member.userId] = member
+ return acc
+ }, {})
+}
+
/**
* Api request for fetching project phases
* @param id Project id
@@ -118,11 +132,8 @@ export async function addUserToProject (projectId, userId, role) {
* @param role
* @returns {Promise<*>}
*/
-export async function inviteUserToProject (projectId, email, role) {
- return createProjectMemberInvite(projectId, {
- emails: [email],
- role: role
- })
+export async function inviteUserToProject (projectId, params) {
+ return createProjectMemberInvite(projectId, params)
}
/**