From cc78293ebde0bebf381f8f6426a280996cc6617f Mon Sep 17 00:00:00 2001 From: Samir Date: Mon, 18 Feb 2019 14:33:33 +0100 Subject: [PATCH 1/4] copilot workflow changes --- .../TeamManagement/TeamManagement.jsx | 15 ++++-- .../TeamManagement/TeamManagement.scss | 5 +- .../TopcoderManagementDialog.js | 27 ++++++++-- src/config/constants.js | 3 ++ .../containers/TeamManagementContainer.jsx | 7 +++ src/projects/reducers/project.js | 8 +-- .../notifications/constants/notifications.js | 53 +++++++++++++++---- .../notifications/helpers/notifications.js | 28 +++++----- .../components/NotificationSettingsForm.jsx | 5 +- 9 files changed, 118 insertions(+), 33 deletions(-) diff --git a/src/components/TeamManagement/TeamManagement.jsx b/src/components/TeamManagement/TeamManagement.jsx index 52447321e..3cc2c909f 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 } = this.props const currentMember = members.filter((member) => member.userId === currentUser.userId)[0] const modalActive = isAddingTeamMember || deletingMember || isShowJoin || showNewMemberConfirmation || deletingInvite @@ -196,8 +196,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) } @@ -214,6 +217,7 @@ class TeamManagement extends React.Component { onCancel={onClickCancel} removeMember={removeMember} addUsers={onTopcoderInviteSend} + approveOrDecline={onAcceptOrRefuse} invites={topcoderTeamInvites} removeInvite={removeInvite} changeRole={changeRole} @@ -370,6 +374,11 @@ TeamManagement.propTypes = { */ onTopcoderInviteSend: PropTypes.func, + /** + * Callback to accept or refuse invite + */ + onAcceptOrRefuse: PropTypes.func, + /** * Callback to send member role */ diff --git a/src/components/TeamManagement/TeamManagement.scss b/src/components/TeamManagement/TeamManagement.scss index 5239b16c2..3067e22c2 100644 --- a/src/components/TeamManagement/TeamManagement.scss +++ b/src/components/TeamManagement/TeamManagement.scss @@ -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; diff --git a/src/components/TeamManagement/TopcoderManagementDialog.js b/src/components/TeamManagement/TopcoderManagementDialog.js index 24a81e2cd..5762c7df5 100644 --- a/src/components/TeamManagement/TopcoderManagementDialog.js +++ b/src/components/TeamManagement/TopcoderManagementDialog.js @@ -112,8 +112,9 @@ class Dialog extends React.Component { } render() { - const {members, currentUser, isMember, removeMember, onCancel, removeInvite, invites = []} = this.props + const {members, currentUser, isMember, removeMember, onCancel, removeInvite, approveOrDecline, invites = []} = this.props const showRemove = currentUser.isAdmin || (isMember && currentUser.isManager) + const showApproveDecline = currentUser.isCopilotManager let i = 0 return ( { 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}` @@ -247,7 +260,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')} @@ -269,7 +289,7 @@ class Dialog extends React.Component { disabled={(!currentUser.isAdmin && !isMember) || this.state.clearText} /> { 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.COPILOT_ADDED, + 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.COPILOT_REFUSED, + type: NOTIFICATION_TYPE.MEMBER_ADDED, + rules: [{ + text: 'Your request to add invite the copilot was refused', + creator: true, + goTo: GOTO.PROJECT_DASHBOARD + }] + }, + { eventType: EVENT_TYPE.MEMBER.COPILOT_JOINED, type: NOTIFICATION_TYPE.MEMBER_ADDED, @@ -288,8 +323,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, @@ -302,8 +337,8 @@ export const NOTIFICATIONS = [ toTopicStarter: true, goTo: GOTO.POST }] - }, - + }, + { version: 2, eventType: EVENT_TYPE.POST.MENTION, @@ -379,7 +414,7 @@ export const NOTIFICATIONS = [ goTo: GOTO.PROJECT_SPECIFICATION }] }, - + { eventType: EVENT_TYPE.PROJECT_PLAN.READY, type: NOTIFICATION_TYPE.UPDATES, @@ -469,7 +504,7 @@ export const NOTIFICATIONS = [ goTo: GOTO.PHASE }] }, - + { eventType: EVENT_TYPE.PROJECT_PLAN.PHASE_PROGRESS_UPDATED, type: NOTIFICATION_TYPE.UPDATES, @@ -524,7 +559,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'} From adf61c5701b3ccff2adb22fb96a042a6fc6694bf Mon Sep 17 00:00:00 2001 From: gondzo Date: Wed, 20 Feb 2019 11:34:28 +0100 Subject: [PATCH 2/4] Update constants.js --- src/config/constants.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/constants.js b/src/config/constants.js index 67139e220..d8e3911f2 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -781,8 +781,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', - COPILOT_ADDED: 'notifications.connect.project.member.copilot.added', - COPILOT_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', From 681c2dbe755aac98285c13844ec67a036b5b024f Mon Sep 17 00:00:00 2001 From: gondzo Date: Wed, 20 Feb 2019 11:35:53 +0100 Subject: [PATCH 3/4] Update notifications.js --- src/routes/notifications/constants/notifications.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/notifications/constants/notifications.js b/src/routes/notifications/constants/notifications.js index 3183a5182..f77be5c27 100644 --- a/src/routes/notifications/constants/notifications.js +++ b/src/routes/notifications/constants/notifications.js @@ -238,22 +238,22 @@ export const NOTIFICATIONS = [ }, { - eventType: EVENT_TYPE.MEMBER.COPILOT_REFUSED, + eventType: EVENT_TYPE.MEMBER.INVITE_REFUSED, type: NOTIFICATION_TYPE.MEMBER_ADDED, rules: [{ - text: 'Your request to add invite the copilot was refused', + text: 'Your request to add invite the member was refused', creator: true, goTo: GOTO.PROJECT_DASHBOARD }] }, { - eventType: EVENT_TYPE.MEMBER.COPILOT_JOINED, + eventType: EVENT_TYPE.MEMBER.INVITE_APPROVED, type: NOTIFICATION_TYPE.MEMBER_ADDED, rules: [{ - text: 'A copilot joined your project team', + text: 'A member joined your project team', shouldBundle: true, - bundledText: '{{bundledCount}} copilots joined your project team', + bundledText: '{{bundledCount}} members joined your project team', projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER], goTo: GOTO.PROJECT_DASHBOARD }] From dc0f42b26df15fc65e5be5c63a50ff27a7475052 Mon Sep 17 00:00:00 2001 From: gondzo Date: Wed, 20 Feb 2019 11:48:48 +0100 Subject: [PATCH 4/4] Update notifications.js --- src/routes/notifications/constants/notifications.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/notifications/constants/notifications.js b/src/routes/notifications/constants/notifications.js index f77be5c27..f85493641 100644 --- a/src/routes/notifications/constants/notifications.js +++ b/src/routes/notifications/constants/notifications.js @@ -224,7 +224,7 @@ export const NOTIFICATIONS = [ }, { - eventType: EVENT_TYPE.MEMBER.COPILOT_ADDED, + eventType: EVENT_TYPE.MEMBER.INVITE_APPROVED, type: NOTIFICATION_TYPE.MEMBER_ADDED, rules: [{ text: 'You are added as a copilot', @@ -248,12 +248,12 @@ export const NOTIFICATIONS = [ }, { - eventType: EVENT_TYPE.MEMBER.INVITE_APPROVED, + eventType: EVENT_TYPE.MEMBER.COPILOT_JOINED, type: NOTIFICATION_TYPE.MEMBER_ADDED, rules: [{ - text: 'A member joined your project team', + text: 'A copilot joined your project team', shouldBundle: true, - bundledText: '{{bundledCount}} members joined your project team', + bundledText: '{{bundledCount}} copilots joined your project team', projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER], goTo: GOTO.PROJECT_DASHBOARD }]