Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions src/components/TeamManagement/TeamManagement.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class TeamManagement extends React.Component {
showNewMemberConfirmation, onJoin, onJoinConfirm, onShowProjectDialog, isShowProjectDialog,
projectTeamInvites, onProjectInviteDeleteConfirm, onProjectInviteSend, deletingInvite, changeRole,
onDeleteInvite, isShowTopcoderDialog, onShowTopcoderDialog, processingInvites, processingMembers,
onTopcoderInviteSend, onTopcoderInviteDeleteConfirm, topcoderTeamInvites, error,
onTopcoderInviteSend, onTopcoderInviteDeleteConfirm, topcoderTeamInvites, onAcceptOrRefuse, error,
onSelectedMembersUpdate, selectedMembers
} = this.props
const currentMember = members.filter((member) => member.userId === currentUser.userId)[0]
Expand Down Expand Up @@ -188,6 +188,7 @@ class TeamManagement extends React.Component {
}
return (
<ProjectDialog
processingInvites={processingInvites}
error={error}
currentUser={currentUser}
members={members}
Expand All @@ -203,8 +204,11 @@ class TeamManagement extends React.Component {
/>
)
})())}
{(!modalActive && isShowTopcoderDialog) && ((() => {
const onClickCancel = () => onShowTopcoderDialog(false)
{(!modalActive && (isShowTopcoderDialog || this.props.history.location.hash === '#manageTopcoderTeam')) && ((() => {
const onClickCancel = () => {
this.props.history.push(this.props.history.location.pathname)
onShowTopcoderDialog(false)
}
const removeMember = (member) => {
onMemberDelete(member)
}
Expand All @@ -213,13 +217,15 @@ class TeamManagement extends React.Component {
}
return (
<TopcoderDialog
processingInvites={processingInvites}
error={error}
currentUser={currentUser}
members={members}
isMember={!!currentMember}
onCancel={onClickCancel}
removeMember={removeMember}
addUsers={onTopcoderInviteSend}
approveOrDecline={onAcceptOrRefuse}
invites={topcoderTeamInvites}
removeInvite={removeInvite}
changeRole={changeRole}
Expand Down Expand Up @@ -389,6 +395,11 @@ TeamManagement.propTypes = {
*/
selectedMembers: PropTypes.arrayOf(PropTypes.object),

/**
* Callback to accept or refuse invite
*/
onAcceptOrRefuse: PropTypes.func,

/**
* Callback to send member role
*/
Expand Down
5 changes: 4 additions & 1 deletion src/components/TeamManagement/TeamManagement.scss
Original file line number Diff line number Diff line change
Expand Up @@ -212,10 +212,13 @@
font-size: $tc-label-sm;
cursor: pointer;

span {
margin-left: $base-unit*3;
}

.email-date {
cursor: default;
color: $tc-gray-40;
margin-left: $base-unit*3;

@media screen and (max-width: $screen-md - 1px) {
display: none;
Expand Down
27 changes: 24 additions & 3 deletions src/components/TeamManagement/TopcoderManagementDialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ class Dialog extends React.Component {

render() {
const {
members, currentUser, isMember, removeMember, onCancel, removeInvite, invites = [],
members, currentUser, isMember, removeMember, onCancel, removeInvite, approveOrDecline, invites = [],
selectedMembers, processingInvites
} = this.props
const showRemove = currentUser.isAdmin || (isMember && currentUser.isManager)
const showApproveDecline = currentUser.isCopilotManager
let i = 0
const allMembers = [...members, ...invites.map(i => i.member)]
return (
Expand Down Expand Up @@ -210,6 +211,18 @@ class Dialog extends React.Component {
const remove = () => {
removeInvite(invite)
}
const approve = () => {
approveOrDecline({
userId: invite.userId,
status: 'accepted'
})
}
const decline = () => {
approveOrDecline({
userId: invite.userId,
status: 'refused'
})
}
const firstName = _.get(invite.member, 'firstName', '')
const lastName = _.get(invite.member, 'lastName', '')
let userFullName = `${firstName} ${lastName}`
Expand All @@ -231,7 +244,14 @@ class Dialog extends React.Component {
@{invite.member.handle || 'ConnectUser'}
</span>
</div>
{showRemove && <div className="member-remove" onClick={remove}>
{showApproveDecline && <div className="member-remove">
<span onClick={approve}>approve</span>
<span onClick={decline}>decline</span>
<span className="email-date">
requested {moment(invite.createdAt).format('MMM D, YY')}
</span>
</div>}
{!showApproveDecline && showRemove && <div className="member-remove" onClick={remove}>
Remove
<span className="email-date">
Invited {moment(invite.createdAt).format('MMM D, YY')}
Expand All @@ -252,7 +272,7 @@ class Dialog extends React.Component {
allMembers={allMembers}
/>
{ this.state.showAlreadyMemberError && <div className="error-message">
Project Member(s) can't be invited again. Please remove them from list.
'Project Member(s) can\'t be invited again. Please remove them from list.'
</div> }
<Formsy.Form>
<SelectDropdown
Expand Down Expand Up @@ -295,6 +315,7 @@ Dialog.propTypes = {
changeRole: PT.func.isRequired,
invites: PT.arrayOf(PT.object),
addUsers: PT.func.isRequired,
approveOrDecline: PT.func.isRequired,
removeInvite: PT.func.isRequired,
onSelectedMembersUpdate: PT.func.isRequired,
selectedMembers: PT.arrayOf(PT.object),
Expand Down
4 changes: 2 additions & 2 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -783,8 +783,8 @@ export const EVENT_TYPE = {
COPILOT_JOINED: 'notifications.connect.project.member.copilotJoined',
ASSIGNED_AS_OWNER: 'notifications.connect.project.member.assignedAsOwner',
INVITE_REQUESTED: 'notifications.connect.project.member.invite.requested',
INVITE_APPROVED: 'notifications.connect.project.member.copilot.added',
INVITE_REFUSED: 'notifications.connect.project.member.copilot.refused',
INVITE_APPROVED: 'notifications.connect.project.member.invite.approved',
INVITE_REFUSED: 'notifications.connect.project.member.invite.refused',
},
PROJECT: {
ACTIVE: 'notifications.connect.project.active',
Expand Down
7 changes: 7 additions & 0 deletions src/projects/detail/containers/TeamManagementContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class TeamManagementContainer extends Component {
this.onTopcoderInviteSend = this.onTopcoderInviteSend.bind(this)
this.onMemberDeleteConfirm = this.onMemberDeleteConfirm.bind(this)
this.onJoinConfirm = this.onJoinConfirm.bind(this)
this.onAcceptOrRefuse = this.onAcceptOrRefuse.bind(this)
this.changeRole = this.changeRole.bind(this)
this.onSelectedMembersUpdate = this.onSelectedMembersUpdate.bind(this)
this.onShowDialog = this.onShowDialog.bind(this)
Expand Down Expand Up @@ -100,6 +101,10 @@ class TeamManagementContainer extends Component {
this.props.inviteProjectMembers(this.props.projectId, emails, handles)
}

onAcceptOrRefuse(invite) {
this.props.acceptOrRefuseInvite(this.props.projectId, invite)
}

changeRole(memberId, item) {
this.props.updateProjectMember(this.props.projectId, memberId, item)
}
Expand Down Expand Up @@ -178,6 +183,7 @@ class TeamManagementContainer extends Component {
<div>
<TeamManagement
{...this.state}
history={this.props.history}
onUserInviteAction={this.onUserInviteAction}
processingMembers={this.props.processingMembers}
processingInvites={this.props.processingInvites}
Expand All @@ -189,6 +195,7 @@ class TeamManagementContainer extends Component {
onMemberDeleteConfirm={this.onMemberDeleteConfirm}
onJoinConfirm={this.onJoinConfirm}
onProjectInviteDeleteConfirm={this.onProjectInviteDelete}
onAcceptOrRefuse={this.onAcceptOrRefuse}
onProjectInviteSend={this.onProjectInviteSend}
onTopcoderInviteDeleteConfirm={this.onTopcoderInviteDelete}
onTopcoderInviteSend={this.onTopcoderInviteSend}
Expand Down
8 changes: 4 additions & 4 deletions src/projects/reducers/project.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
INVITE_TOPCODER_MEMBER_SUCCESS, REMOVE_TOPCODER_MEMBER_INVITE_SUCCESS, INVITE_TOPCODER_MEMBER_PENDING, REMOVE_CUSTOMER_INVITE_PENDING,
REMOVE_TOPCODER_MEMBER_INVITE_PENDING, REMOVE_TOPCODER_MEMBER_INVITE_FAILURE, REMOVE_CUSTOMER_INVITE_FAILURE,
INVITE_CUSTOMER_FAILURE, INVITE_TOPCODER_MEMBER_FAILURE, INVITE_CUSTOMER_PENDING,
ACCEPT_OR_REFUSE_INVITE_SUCCESS, ACCEPT_OR_REFUSE_INVITE_FAILURE, ACCEPT_OR_REFUSE_INVITE_PENDING,
ACCEPT_OR_REFUSE_INVITE_SUCCESS, ACCEPT_OR_REFUSE_INVITE_FAILURE, ACCEPT_OR_REFUSE_INVITE_PENDING,
UPLOAD_PROJECT_ATTACHMENT_FILES, DISCARD_PROJECT_ATTACHMENT, CHANGE_ATTACHMENT_PERMISSION
} from '../../config/constants'
import _ from 'lodash'
Expand Down Expand Up @@ -165,7 +165,7 @@ export const projectState = function (state=initialState, action) {

case LOAD_PROJECT_MEMBER_INVITES_SUCCESS: {
return Object.assign({}, state, {
showUserInvited: true
showUserInvited: true && action.payload.role !== 'copilot'
})
}

Expand All @@ -177,7 +177,7 @@ export const projectState = function (state=initialState, action) {

case ACCEPT_OR_REFUSE_INVITE_PENDING:
return Object.assign({}, state, {
showUserInvited: true
showUserInvited: false
})

case ACCEPT_OR_REFUSE_INVITE_SUCCESS: {
Expand Down Expand Up @@ -388,7 +388,7 @@ export const projectState = function (state=initialState, action) {
attachmentsAwaitingPermission: action.payload,
attachmentPermissions: null
}

case DISCARD_PROJECT_ATTACHMENT:
return {
...state,
Expand Down
55 changes: 45 additions & 10 deletions src/routes/notifications/constants/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/
import {
NOTIFICATION_TYPE,
ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, ROLE_CONNECT_ACCOUNT_MANAGER, ROLE_ADMINISTRATOR,
ROLE_CONNECT_COPILOT, ROLE_CONNECT_MANAGER, ROLE_CONNECT_ACCOUNT_MANAGER, ROLE_CONNECT_COPILOT_MANAGER, ROLE_ADMINISTRATOR,
PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_OWNER, PROJECT_ROLE_MEMBER,
EVENT_TYPE,
} from '../../../config/constants'
Expand All @@ -18,7 +18,8 @@ export const GOTO = {
TOPIC: '/projects/{{projectId}}/#feed-{{topicId}}',
POST: '/projects/{{projectId}}/#comment-{{postId}}',
FILE_LIST: '/projects/{{projectId}}/specification#appDefinition-files',
PHASE: '/projects/{{projectId}}/plan#phase-{{phaseId}}'
PHASE: '/projects/{{projectId}}/plan#phase-{{phaseId}}',
TOPCODER_TEAM: '/projects/{{projectId}}#manageTopcoderTeam'
}

// each notification can be displayed differently depend on WHO see them
Expand Down Expand Up @@ -217,11 +218,45 @@ export const NOTIFICATIONS = [
}]
},

{
eventType: EVENT_TYPE.MEMBER.INVITE_REQUESTED,
type: NOTIFICATION_TYPE.MEMBER_ADDED,
rules: [{
text: 'You are requested to add <strong>{{userFullName}}</strong> as a copilot',
topcoderRoles: [ROLE_CONNECT_COPILOT_MANAGER],
goTo: GOTO.TOPCODER_TEAM
}]
},

{
eventType: EVENT_TYPE.MEMBER.INVITE_APPROVED,
type: NOTIFICATION_TYPE.MEMBER_ADDED,
rules: [{
text: 'You are added as a copilot',
toUserHandle: true,
goTo: GOTO.PROJECT_DASHBOARD
}, {
text: 'Your request to add invite the copilot was approved',
creator: true,
goTo: GOTO.PROJECT_DASHBOARD
}]
},

{
eventType: EVENT_TYPE.MEMBER.INVITE_REFUSED,
type: NOTIFICATION_TYPE.MEMBER_ADDED,
rules: [{
text: 'Your request to add invite the member was refused',
creator: true,
goTo: GOTO.PROJECT_DASHBOARD
}]
},

{
eventType: EVENT_TYPE.MEMBER.COPILOT_JOINED,
type: NOTIFICATION_TYPE.MEMBER_ADDED,
rules: [{
text: 'A copilot joined your project team',
text: 'A copilot joined your project team',
shouldBundle: true,
bundledText: '{{bundledCount}} copilots joined your project team',
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER],
Expand Down Expand Up @@ -293,8 +328,8 @@ export const NOTIFICATIONS = [
projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER],
goTo: GOTO.POST
}]
},
},

{
version: 2,
eventType: EVENT_TYPE.POST.UPDATED,
Expand All @@ -307,8 +342,8 @@ export const NOTIFICATIONS = [
toTopicStarter: true,
goTo: GOTO.POST
}]
},
},

{
version: 2,
eventType: EVENT_TYPE.POST.MENTION,
Expand Down Expand Up @@ -384,7 +419,7 @@ export const NOTIFICATIONS = [
goTo: GOTO.PROJECT_SPECIFICATION
}]
},

{
eventType: EVENT_TYPE.PROJECT_PLAN.READY,
type: NOTIFICATION_TYPE.UPDATES,
Expand Down Expand Up @@ -474,7 +509,7 @@ export const NOTIFICATIONS = [
goTo: GOTO.PHASE
}]
},

{
eventType: EVENT_TYPE.PROJECT_PLAN.PHASE_PROGRESS_UPDATED,
type: NOTIFICATION_TYPE.UPDATES,
Expand Down Expand Up @@ -529,7 +564,7 @@ export const NOTIFICATIONS = [
goTo: GOTO.PROJECT_PLAN
}]
},

{
eventType: EVENT_TYPE.PROJECT_PLAN.TIMELINE_ADJUSTED,
type: NOTIFICATION_TYPE.UPDATES,
Expand Down
Loading