diff --git a/src/components/TeamManagement/TeamManagement.jsx b/src/components/TeamManagement/TeamManagement.jsx index 925abd6a0..fbea7b6e2 100644 --- a/src/components/TeamManagement/TeamManagement.jsx +++ b/src/components/TeamManagement/TeamManagement.jsx @@ -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] @@ -188,6 +188,7 @@ class TeamManagement extends React.Component { } return ( ) })())} - {(!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) } @@ -213,6 +217,7 @@ class TeamManagement extends React.Component { } return ( i.member)] return ( @@ -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}` @@ -231,7 +244,14 @@ class Dialog extends React.Component { @{invite.member.handle || 'ConnectUser'} - {showRemove &&
+ {showApproveDecline &&
+ approve + decline + + requested {moment(invite.createdAt).format('MMM D, YY')} + +
} + {!showApproveDecline && showRemove &&
Remove Invited {moment(invite.createdAt).format('MMM D, YY')} @@ -252,7 +272,7 @@ class Dialog extends React.Component { allMembers={allMembers} /> { this.state.showAlreadyMemberError &&
- 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.'
} {{userFullName}} 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], @@ -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, @@ -307,8 +342,8 @@ export const NOTIFICATIONS = [ toTopicStarter: true, goTo: GOTO.POST }] - }, - + }, + { version: 2, eventType: EVENT_TYPE.POST.MENTION, @@ -384,7 +419,7 @@ export const NOTIFICATIONS = [ goTo: GOTO.PROJECT_SPECIFICATION }] }, - + { eventType: EVENT_TYPE.PROJECT_PLAN.READY, type: NOTIFICATION_TYPE.UPDATES, @@ -474,7 +509,7 @@ export const NOTIFICATIONS = [ goTo: GOTO.PHASE }] }, - + { eventType: EVENT_TYPE.PROJECT_PLAN.PHASE_PROGRESS_UPDATED, type: NOTIFICATION_TYPE.UPDATES, @@ -529,7 +564,7 @@ export const NOTIFICATIONS = [ goTo: GOTO.PROJECT_PLAN }] }, - + { eventType: EVENT_TYPE.PROJECT_PLAN.TIMELINE_ADJUSTED, type: NOTIFICATION_TYPE.UPDATES, diff --git a/src/routes/notifications/helpers/notifications.js b/src/routes/notifications/helpers/notifications.js index ff1b4c99f..aaddd56ba 100644 --- a/src/routes/notifications/helpers/notifications.js +++ b/src/routes/notifications/helpers/notifications.js @@ -66,10 +66,10 @@ export const renderGoTo = (goToHandlebars, contents) => ( /** * Filter notifications by criteria - * + * * @param {Array} notifications notifications list * @param {Object} criteria criteria to filter notifications - * + * * @returns {Array} notifiations which meet the criteria */ export const filterNotificationsByCriteria = (notifications, criteria) => { @@ -85,7 +85,7 @@ export const isSubEqual = (object, criteria) => { } else { isEqual = !!object && value === object[key] } - + return isEqual }) @@ -160,7 +160,7 @@ const compareSourcesByLastNotificationDate = (s1, s2) => { export const splitNotificationsBySources = (sources, notifications) => { const notificationsBySources = sources.map(source => { const sourceNotifications = _.filter(notifications, { sourceId: source.id }) - + return ({ ...source, notifications: sourceNotifications, @@ -290,6 +290,10 @@ const getNotificationRule = (notification) => { match = match && _notificationRule.toUserHandle } + if (notification.contents.creator) { + match = match && _notificationRule.creator + } + if (notification.contents.projectRole) { match = match && _notificationRule.projectRoles && _.includes(_notificationRule.projectRoles, notification.contents.projectRole) } @@ -318,7 +322,7 @@ const isNotificationRuleEqual = (rule1, rule2) => { * * @type {Array} */ - const ESSENTIAL_RULE_PROPERTIES = ['eventType', 'toTopicStarter', 'toUserHandle', 'projectRole', 'topcoderRole'] + const ESSENTIAL_RULE_PROPERTIES = ['eventType', 'toTopicStarter', 'toUserHandle', 'projectRole', 'topcoderRole', 'creator'] const essentialRule1 = _.pick(rule1, ESSENTIAL_RULE_PROPERTIES) const essentialRule2 = _.pick(rule2, ESSENTIAL_RULE_PROPERTIES) @@ -410,7 +414,7 @@ export const prepareNotifications = (rawNotifications) => { contents: rawNotification.contents, version: rawNotification.version })) - + // populate notifications with additional properties // - type // - goto @@ -427,7 +431,7 @@ export const prepareNotifications = (rawNotifications) => { if (notificationRule.goTo) { notification.goto = renderGoTo(notificationRule.goTo, notification.contents) } - + notification.rule = notificationRule } }) @@ -437,9 +441,9 @@ export const prepareNotifications = (rawNotifications) => { /** * Bundle notifications and renders notifications texts - * + * * @param {Array} notifications notifications list - * + * * @returns {Array} notifications list with rendered texts */ export const preRenderNotifications = (notifications) => { @@ -466,7 +470,7 @@ export const preRenderNotifications = (notifications) => { }) const preRenderedNotifications = _.map(bundledNotificationsWithRules, 'notification') - + // sort notifications by date (newer first) preRenderedNotifications.sort((n1, n2) => { const date1 = new Date(n1.date).getTime() @@ -474,6 +478,6 @@ export const preRenderNotifications = (notifications) => { return date2 - date1 }) - + return preRenderedNotifications -} \ No newline at end of file +} diff --git a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx index 1a0a6ee2b..743a06146 100644 --- a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx +++ b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx @@ -75,6 +75,9 @@ const topics = [ EVENT_TYPE.MEMBER.MANAGER_JOINED, EVENT_TYPE.MEMBER.COPILOT_JOINED, EVENT_TYPE.MEMBER.ASSIGNED_AS_OWNER, + EVENT_TYPE.MEMBER.INVITE_REQUESTED, + EVENT_TYPE.MEMBER.COPILOT_ADDED, + EVENT_TYPE.MEMBER.COPILOT_REFUSED, ] }, { title: 'Project plan', @@ -308,7 +311,7 @@ class NotificationSettingsForm extends React.Component { ) : ( - this.handleWebConfigurationChange(index)} name={`web[${index}]`} checked={notifications[topicFirstType].web.enabled === 'yes'}