Skip to content

Commit de90e5d

Browse files
committed
Merge branch 'cf19' into hotfix/post-release-2.4.15.2
# Conflicts: # src/config/constants.js # src/projects/actions/projectMember.js
2 parents b69081b + ff7733d commit de90e5d

File tree

9 files changed

+242
-26
lines changed

9 files changed

+242
-26
lines changed

src/components/TeamManagement/TeamManagement.scss

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,45 @@
453453
}
454454
}
455455

456+
.decline-btn {
457+
@include roboto;
458+
cursor: pointer;
459+
padding: $base-unit*2 $base-unit*3;
460+
display: flex;
461+
align-items: center;
462+
height: 40px;
463+
border: 1px solid $tc-gray-10;
464+
border-radius: 20px !important;
465+
margin: $base-unit*2 0 $base-unit 0;
466+
color: $tc-gray-70;
467+
font-size: $tc-label-md;
468+
text-align: center;
469+
justify-content: center;
470+
width: 130px;
471+
margin: 5px;
472+
473+
svg {
474+
margin-right: $base-unit * 0.5;
475+
transform: scale(.6);
476+
fill: $tc-gray-90;
477+
}
478+
479+
&.in-grid {
480+
width: 103px;
481+
margin: 0 5px 5px 5px;
482+
}
483+
}
484+
485+
.accept-btn {
486+
border-radius: 20px !important;
487+
white-space: nowrap;
488+
margin: 5px;
489+
490+
&.in-grid {
491+
padding: 0px 11px !important;
492+
}
493+
}
494+
456495
.join-btn {
457496
@include roboto;
458497
cursor: pointer;
@@ -472,6 +511,7 @@
472511
fill: $tc-gray-90;
473512
}
474513
}
514+
475515
}
476516

477517
.separator {

src/config/constants.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,21 @@ export const ROLE_PROGRAM_MANAGER = 'Program Manager'
652652
export const ROLE_SOLUTION_ARCHITECT = 'Solution Architect'
653653
export const ROLE_PROJECT_MANAGER = 'Project Manager'
654654

655+
export const ADMIN_ROLES = [ROLE_ADMINISTRATOR, ROLE_CONNECT_ADMIN]
656+
657+
export const MANAGER_ROLES = [
658+
...ADMIN_ROLES,
659+
ROLE_CONNECT_MANAGER,
660+
ROLE_CONNECT_ACCOUNT_MANAGER,
661+
ROLE_CONNECT_COPILOT_MANAGER,
662+
ROLE_BUSINESS_DEVELOPMENT_REPRESENTATIVE,
663+
ROLE_PRESALES,
664+
ROLE_ACCOUNT_EXECUTIVE,
665+
ROLE_PROGRAM_MANAGER,
666+
ROLE_SOLUTION_ARCHITECT,
667+
ROLE_PROJECT_MANAGER,
668+
]
669+
655670
// to be able to start the Connect App we should pass at least the dummy value for `FILE_PICKER_API_KEY`
656671
// but if we want to test file uploading we should provide the real value in `FILE_PICKER_API_KEY` env variable
657672
export const FILE_PICKER_API_KEY = process.env.FILE_PICKER_API_KEY || 'DUMMY'

src/projects/actions/projectMember.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,18 @@ export function inviteProjectMembers(projectId, emailIds, handles) {
161161
}
162162
}
163163

164-
export function acceptOrRefuseInvite(projectId, item) {
164+
/**
165+
* Accept or refuse invite
166+
* @param {Number} projectId project id
167+
* @param {Object} item accept or refuse invite info
168+
* @param {Object} currentUser current user info
169+
*/
170+
export function acceptOrRefuseInvite(projectId, item, currentUser) {
165171
return (dispatch) => {
166172
return dispatch({
167173
type: ACCEPT_OR_REFUSE_INVITE,
168-
payload: updateProjectMemberInvite(projectId, item)
174+
payload: updateProjectMemberInvite(projectId, item),
175+
meta: { projectId, currentUser },
169176
})
170177
}
171178
}

src/projects/components/projectsCard/ProjectCard.jsx

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import React from 'react'
22
import PT from 'prop-types'
33
import _ from 'lodash'
4-
import { withRouter, Link } from 'react-router-dom'
4+
import { withRouter } from 'react-router-dom'
55
import { getProjectRoleForCurrentUser } from '../../../helpers/projectHelper'
66
import ProjectCardHeader from './ProjectCardHeader'
77
import ProjectCardBody from './ProjectCardBody'
88
import ProjectManagerAvatars from '../../list/components/Projects/ProjectManagerAvatars'
9+
import LoadingIndicator from '../../../components/LoadingIndicator/LoadingIndicator'
910
import './ProjectCard.scss'
1011

11-
function ProjectCard({ project, duration, disabled, currentUser, history, onChangeStatus, projectTemplates, unreadMentionsCount }) {
12+
function ProjectCard({ project, duration, disabled, currentUser, history, onChangeStatus, projectTemplates, unreadMentionsCount, callInviteRequest, isAcceptingInvite }) {
1213
const className = `ProjectCard ${ disabled ? 'disabled' : 'enabled'}`
1314
if (!project) return null
1415
const currentMemberRole = getProjectRoleForCurrentUser({ project, currentUserId: currentUser.userId})
@@ -41,11 +42,27 @@ function ProjectCard({ project, duration, disabled, currentUser, history, onChan
4142
<ProjectManagerAvatars managers={project.members} maxShownNum={10} />
4243
<div>
4344
{(!isMember && isInvited) &&
44-
<Link to={`/projects/${project.id}`} className="spacing">
45-
<div className="join-btn" style={{margin: '5px'}}>
46-
Join project
47-
</div>
48-
</Link>
45+
<div className="spacing join-btn-container">
46+
{(!isAcceptingInvite) && (
47+
<button
48+
onClick={(event) => {
49+
event.stopPropagation()
50+
callInviteRequest(project, true)
51+
}}
52+
className={'tc-btn tc-btn-primary tc-btn-md blue accept-btn'}
53+
>
54+
Join project
55+
</button>)}
56+
{(!isAcceptingInvite) && (
57+
<button
58+
onClick={(event) => {
59+
event.stopPropagation()
60+
callInviteRequest(project, false)
61+
}}
62+
className="decline-btn"
63+
>Decline</button>)}
64+
{isAcceptingInvite && (<LoadingIndicator isSmall />)}
65+
</div>
4966
}
5067
</div>
5168
</div>
@@ -54,13 +71,17 @@ function ProjectCard({ project, duration, disabled, currentUser, history, onChan
5471
}
5572

5673
ProjectCard.defaultTypes = {
74+
callInviteRequest: () => {},
75+
isAcceptingInvite: false,
5776
}
5877

5978
ProjectCard.propTypes = {
6079
project: PT.object.isRequired,
6180
currentMemberRole: PT.string,
6281
projectTemplates: PT.array.isRequired,
63-
unreadMentionsCount: PT.number.isRequired
82+
unreadMentionsCount: PT.number.isRequired,
83+
callInviteRequest: PT.func,
84+
isAcceptingInvite: PT.bool
6485
// duration: PT.object.isRequired,
6586
}
6687

src/projects/components/projectsCard/ProjectCard.scss

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,15 @@
8484

8585
.card-footer {
8686
// padding-top: 4 * $base_unit;
87+
display: flex;
88+
justify-content: space-between;
89+
align-items: flex-end;
8790
}
8891

89-
.join-btn {
90-
float: right;
92+
.join-btn-container {
93+
display: flex;
94+
align-items: flex-end;
95+
flex-direction: column;
9196
}
9297
}
9398
}

src/projects/components/projectsCard/ProjectsCardView.jsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const ProjectsCardView = props => {
2020
//const { projects, members, totalCount, criteria, pageNum, applyFilters, sortHandler, onPageChange, error, isLoading, onNewProjectIntent } = props
2121
// TODO: use applyFilters and onNewProjectIntent. Temporary delete to avoid lint errors.
2222
const { projects, members, currentUser, onPageChange, pageNum, totalCount, infiniteAutoload, newProjectLink,
23-
setInfiniteAutoload, isLoading, onChangeStatus, projectsStatus, projectTemplates, applyFilters, notifications } = props
23+
setInfiniteAutoload, isLoading, onChangeStatus, projectsStatus, projectTemplates, applyFilters, notifications,
24+
callInviteRequest, isAcceptingInvite } = props
2425
// const currentSortField = _.get(criteria, 'sort', '')
2526

2627
// annotate projects with member data
@@ -50,6 +51,8 @@ const ProjectsCardView = props => {
5051
onChangeStatus={onChangeStatus}
5152
projectTemplates={projectTemplates}
5253
unreadMentionsCount={unreadMentionsCount}
54+
callInviteRequest={callInviteRequest}
55+
isAcceptingInvite={isAcceptingInvite[project.id]}
5356
/>
5457
</div>)
5558
}
@@ -97,7 +100,7 @@ const ProjectsCardView = props => {
97100
hasMore={hasMore}
98101
threshold={500}
99102
>
100-
{ [...projects, ...placeholders].map(renderProject)}
103+
{ [...projects, ...placeholders].map((project) => renderProject(project))}
101104
{moreProject(true)}
102105
<div className="project-card project-card-new">
103106
<NewProjectCard link={newProjectLink} />
@@ -129,6 +132,8 @@ ProjectsCardView.propTypes = {
129132
applyFilters: PropTypes.func,
130133
isLoading: PropTypes.bool,
131134
projectTemplates: PropTypes.array.isRequired,
135+
callInviteRequest: PropTypes.func,
136+
isAcceptingInvite: PropTypes.object,
132137
}
133138

134139

src/projects/list/components/Projects/Projects.jsx

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import CoderBot from '../../../../components/CoderBot/CoderBot'
99
import ProjectListNavHeader from './ProjectListNavHeader'
1010
import ProjectsGridView from './ProjectsGridView'
1111
import ProjectsCardView from '../../../components/projectsCard/ProjectsCardView'
12+
import { acceptOrRefuseInvite } from '../../../actions/projectMember'
1213
import { loadProjects, setInfiniteAutoload, setProjectsListView } from '../../../actions/loadProjects'
1314
import { loadProjectsMetadata } from '../../../../actions/templates'
1415
import { sortProjects } from '../../../actions/sortProjects'
@@ -18,7 +19,9 @@ import { updateProject } from '../../../actions/project'
1819
import { getNewProjectLink } from '../../../../helpers/projectHelper'
1920
import { ROLE_CONNECT_MANAGER, ROLE_CONNECT_ACCOUNT_MANAGER, ROLE_CONNECT_COPILOT, ROLE_ADMINISTRATOR,
2021
ROLE_CONNECT_ADMIN, PROJECT_STATUS, PROJECT_STATUS_CANCELLED,
21-
PROJECT_LIST_DEFAULT_CRITERIA, PROJECTS_LIST_VIEW, PROJECTS_LIST_PER_PAGE, SCREEN_BREAKPOINT_MD } from '../../../../config/constants'
22+
PROJECT_LIST_DEFAULT_CRITERIA, PROJECTS_LIST_VIEW, PROJECTS_LIST_PER_PAGE, SCREEN_BREAKPOINT_MD,
23+
PROJECT_MEMBER_INVITE_STATUS_ACCEPTED, PROJECT_MEMBER_INVITE_STATUS_REFUSED,
24+
} from '../../../../config/constants'
2225
import TwoColsLayout from '../../../../components/TwoColsLayout'
2326
import UserSidebar from '../../../../components/UserSidebar/UserSidebar'
2427

@@ -37,7 +40,9 @@ const EnhancedCards = dataLoadHandler(errorHandler(ProjectsCardView))
3740
class Projects extends Component {
3841
constructor(props) {
3942
super(props)
40-
this.state = {}
43+
this.state = {
44+
isAcceptingInvite: {}
45+
}
4146
this.sortHandler = this.sortHandler.bind(this)
4247
this.onChangeStatus = this.onChangeStatus.bind(this)
4348
this.onPageChange = this.onPageChange.bind(this)
@@ -47,6 +52,7 @@ class Projects extends Component {
4752
this.changeView = this.changeView.bind(this)
4853
this.init = this.init.bind(this)
4954
this.removeScrollPosition = this.removeScrollPosition.bind(this)
55+
this.callInviteRequest = this.callInviteRequest.bind(this)
5056
}
5157

5258
componentWillUnmount(){
@@ -191,9 +197,37 @@ class Projects extends Component {
191197
this.props.loadProjects(criteria)
192198
}
193199

200+
/**
201+
* Call request to accept or decline invite
202+
* @param {Object} project project info
203+
* @param {Bool} isAccept is accept invite
204+
*/
205+
callInviteRequest(project, isAccept) {
206+
const isAcceptingInvite = (isAcception) => {
207+
const { isAcceptingInvite } = this.state
208+
isAcceptingInvite[project.id] = isAcception
209+
this.setState({ isAcceptingInvite })
210+
}
211+
212+
isAcceptingInvite(true)
213+
214+
this.props.acceptOrRefuseInvite(project.id, {
215+
userId: this.props.currentUser.userId,
216+
email: this.props.currentUser.email,
217+
status: isAccept ? PROJECT_MEMBER_INVITE_STATUS_ACCEPTED : PROJECT_MEMBER_INVITE_STATUS_REFUSED
218+
}, this.props.currentUser).then(() => {
219+
isAcceptingInvite(false)
220+
}) .catch((err) => {
221+
// if we fail to accept invite
222+
console.log(err)
223+
isAcceptingInvite(false)
224+
})
225+
}
226+
194227
render() {
195228
const { isPowerUser, isCustomer, isLoading, totalCount, criteria, projectsListView, setProjectsListView,
196229
setInfiniteAutoload, loadProjects, history, orgConfig, allProjectsCount, user } = this.props
230+
const { isAcceptingInvite } = this.state
197231
// show walk through if user is customer and no projects were returned
198232
// for default filters
199233
const showWalkThrough = !isLoading && !isPowerUser && totalCount === 0 && allProjectsCount === 0 &&
@@ -212,6 +246,8 @@ class Projects extends Component {
212246
setFilter={this.setFilter}
213247
criteria={criteria}
214248
isCustomer={isCustomer}
249+
callInviteRequest={this.callInviteRequest}
250+
isAcceptingInvite={isAcceptingInvite}
215251
/>
216252
)
217253
const cardView = (
@@ -224,6 +260,8 @@ class Projects extends Component {
224260
onChangeStatus={this.onChangeStatus}
225261
projectsStatus={getStatusCriteriaText(criteria)}
226262
newProjectLink={getNewProjectLink(orgConfig)}
263+
callInviteRequest={this.callInviteRequest}
264+
isAcceptingInvite={isAcceptingInvite}
227265
/>
228266
)
229267
let projectsView
@@ -324,6 +362,14 @@ const mapStateToProps = ({ projectSearch, members, loadUser, projectState, templ
324362
}
325363
}
326364

327-
const actionsToBind = { loadProjects, setInfiniteAutoload, updateProject, setProjectsListView, sortProjects, loadProjectsMetadata }
365+
const actionsToBind = {
366+
loadProjects,
367+
setInfiniteAutoload,
368+
updateProject,
369+
setProjectsListView,
370+
sortProjects,
371+
loadProjectsMetadata,
372+
acceptOrRefuseInvite
373+
}
328374

329375
export default withRouter(connect(mapStateToProps, actionsToBind)(Projects))

0 commit comments

Comments
 (0)