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
Original file line number Diff line number Diff line change
Expand Up @@ -191,21 +191,21 @@ class ManageMilestones extends React.Component {
if (!challengeIds.length) {
return [
<MilestoneChallengeHeader key="header" isUpdatable={isUpdatable}/>,
<MilestoneChallengeRow isEmpty key="row" isUpdatable={isUpdatable}/>
<MilestoneChallengeRow milestone={milestone} isEmpty key="row" isUpdatable={isUpdatable}/>
]
}

// loading challenges
if (milestone.isLoadingChallenges) {
return [
<MilestoneChallengeHeader key="header" isUpdatable={isUpdatable}/>,
<MilestoneChallengeRow isLoading key="row" isUpdatable={isUpdatable}/>,
<MilestoneChallengeRow milestone={milestone} isLoading key="row" isUpdatable={isUpdatable}/>,
<MilestoneChallengeFooter isLoading key="footer" onLoadChallengesByPage={this.onLoadChallengesByPage} isUpdatable={isUpdatable}/>
]
}

const rows = _.map(milestone.challenges, (c) => {
return <MilestoneChallengeRow key={c.id} challenge={c} isUpdatable={isUpdatable}/>
return <MilestoneChallengeRow milestone={milestone} key={c.id} challenge={c} isUpdatable={isUpdatable}/>
})
return [
<MilestoneChallengeHeader key="header" isUpdatable={isUpdatable}/>,
Expand Down Expand Up @@ -248,6 +248,7 @@ class ManageMilestones extends React.Component {
} = this.props

const canEdit = isUpdatable && this.getSelectCount() > 0
const disableDeleteAction = this.getSelectCount() > 1
return (
<div>
<div styleName="toolbar">
Expand Down Expand Up @@ -309,6 +310,7 @@ class ManageMilestones extends React.Component {
allMilestones={milestones}
isCreatingRow={`${milestone.id}`.startsWith('new-milestone')}
isUpdatable={isUpdatable}
disableDeleteAction={disableDeleteAction}
phaseMembers={milestone.members}
/>,
...this.renderChallengeTable(milestone)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,95 +11,124 @@ import IconXMark from '../../../../../../assets/icons/x-mark-thin.svg'

import './AddCopilotsSidebar.scss'

function AddCopilotsSidebar({
memberToAdd,
setMemberToAdd,
copilots,
projectMembers,
onClose,
onAdd,
onRemove
}) {
const canManageCopilots = hasPermission(PERMISSIONS.MANAGE_COPILOTS)
const canRemoveCopilots = hasPermission(PERMISSIONS.REMOVE_COPILOTS)
class AddCopilotsSidebar extends React.Component {
constructor(props) {
super(props)

const projectMemberOptions = projectMembers.map(projectMember => ({
label: projectMember.handle,
value: projectMember
})).filter(
// check if project member was not added
option => copilots.findIndex(copilot => copilot.userId === option.value.userId) === -1 &&
// check if project member role is copilot
option.value.role === PROJECT_ROLE_COPILOT
)
this.state = {
}

return (
<aside styleName="add-copilots-sidebar">
<h2 styleName="title">
Copilot
<button type="button" className="tc-btn tc-btn-link" styleName="button-close" onClick={onClose}>
<IconXMark />
</button>
</h2>
<div styleName="select-copilot">
<span styleName="label">Add New Copilot</span>
<Select
options={projectMemberOptions}
onChange={(selectedOption) => {
if (!selectedOption) {
return
}
this.onClickOutside = this.onClickOutside.bind(this)
}

setMemberToAdd(selectedOption.value)
}}
value={projectMemberOptions.find(option => option.value === memberToAdd) || null}
placeholder="- Select -"
isClearable={false}
/>
<button
className="tc-btn tc-btn-primary tc-btn-sm"
onClick={() => {
onAdd(memberToAdd)
setMemberToAdd(null)
}}
disabled={!memberToAdd}
styleName="add-button"
>
ADD
</button>
</div>
<ul styleName="copilot-list">
{copilots.map((member, index) => (
<li key={`${member.handle}-${index}`}>
<div styleName="member-details">
<Avatar
userName={getFullNameWithFallback(member)}
avatarUrl={getAvatarResized(_.get(member, 'photoURL') || '', 80)}
size={40}
/>
<div styleName="name-and-handle">
<span styleName="fullname">{getFullNameWithFallback(member)}</span>
<span styleName="handle">
@{member.handle || 'ConnectUser'}
</span>
onClickOutside(event) {
const {onClose} = this.props
if(this.ref.contains(event.target)) {
return
}

onClose()
}

componentDidMount() {
document.addEventListener('click', this.onClickOutside)
}

componentWillUnmount() {
document.removeEventListener('click', this.onClickOutside)
}

render() {
const {
memberToAdd,
setMemberToAdd,
copilots,
projectMembers,
onClose,
onAdd,
onRemove
} = this.props
const canManageCopilots = hasPermission(PERMISSIONS.MANAGE_COPILOTS)
const canRemoveCopilots = hasPermission(PERMISSIONS.REMOVE_COPILOTS)

const projectMemberOptions = projectMembers.map(projectMember => ({
label: projectMember.handle,
value: projectMember
})).filter(
// check if project member was not added
option => copilots.findIndex(copilot => copilot.userId === option.value.userId) === -1 &&
// check if project member role is copilot
option.value.role === PROJECT_ROLE_COPILOT
)

return (
<aside styleName="add-copilots-sidebar" ref={ ref => this.ref = ref}>
<h2 styleName="title">
Copilot
<button type="button" className="tc-btn tc-btn-link" styleName="button-close" onClick={onClose}>
<IconXMark />
</button>
</h2>
<div styleName="select-copilot">
<span styleName="label">Add New Copilot</span>
<Select
options={projectMemberOptions}
onChange={(selectedOption) => {
if (!selectedOption) {
return
}

setMemberToAdd(selectedOption.value)
}}
value={projectMemberOptions.find(option => option.value === memberToAdd) || null}
placeholder="- Select -"
isClearable={false}
/>
<button
className="tc-btn tc-btn-primary tc-btn-sm"
onClick={() => {
onAdd(memberToAdd)
setMemberToAdd(null)
}}
disabled={!memberToAdd}
styleName="add-button"
>
ADD
</button>
</div>
<ul styleName="copilot-list">
{copilots.map((member, index) => (
<li key={`${member.handle}-${index}`}>
<div styleName="member-details">
<Avatar
userName={getFullNameWithFallback(member)}
avatarUrl={getAvatarResized(_.get(member, 'photoURL') || '', 80)}
size={40}
/>
<div styleName="name-and-handle">
<span styleName="fullname">{getFullNameWithFallback(member)}</span>
<span styleName="handle">
@{member.handle || 'ConnectUser'}
</span>
</div>
{(canManageCopilots || canRemoveCopilots) && (
<button
className="tc-btn tc-btn-link"
styleName="close-button"
onClick={() => {
onRemove(member)
}}
>
&times;
</button>
)}
</div>
{(canManageCopilots || canRemoveCopilots) && (
<button
className="tc-btn tc-btn-link"
styleName="close-button"
onClick={() => {
onRemove(member)
}}
>
&times;
</button>
)}
</div>
</li>
))}
</ul>
</aside>
)
</li>
))}
</ul>
</aside>
)
}
}

AddCopilotsSidebar.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class MilestoneChallengeFooter extends React.Component {
<td colSpan={isUpdatable? '9': '8'}>
<div styleName="challenge-table-row">
<div styleName="view-button">
<a href={url}>
<a href={url} target="_blank">
VIEW CHALLENGES
</a>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import React from 'react'
import PT from 'prop-types'
import moment from 'moment'
import {
CHALLENGE_DETAIL_APP
WORK_MANAGER_APP
} from '../../../../../../../src/config/constants'

import './MilestoneChallengeRow.scss'

function MilestoneChallengeRow({challenge, isEmpty, isLoading, isUpdatable}) {
function MilestoneChallengeRow({challenge, milestone, isEmpty, isLoading, isUpdatable}) {

if (isEmpty) {
return (
Expand Down Expand Up @@ -41,16 +41,17 @@ function MilestoneChallengeRow({challenge, isEmpty, isLoading, isUpdatable}) {
status,
track,
startDate,
endDate
endDate,
} = challenge

const statusLabel = status.indexOf('Cancelled') === 0 ? 'Cancelled': status
const url = `${WORK_MANAGER_APP}/${milestone.projectId}/challenges/${id}/view`

return (
<tr styleName="challenge-table-row-wrap">
<td colSpan={isUpdatable? '9': '8'}>
<div styleName="challenge-table-row">
<div styleName="title"><a href={`${CHALLENGE_DETAIL_APP}/${id}`}>{name}</a></div>
<div styleName="title"><a href={`${url}`} target="_blank">{name}</a></div>
<div styleName="status"><div styleName={statusLabel}>{statusLabel}</div></div>
<div styleName="type"><div styleName={track.split(' ').join('')}>{track}</div></div>
<div styleName="start-date">{moment(startDate).format('MM-DD-YYYY')}</div>
Expand All @@ -65,7 +66,8 @@ MilestoneChallengeRow.propTypes = {
challenge: PT.shape(),
isUpdatable: PT.bool,
isEmpty: PT.bool,
isLoading: PT.bool
isLoading: PT.bool,
milestone: PT.shape()
}

export default MilestoneChallengeRow
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class MilestoneDeleteButton extends React.Component {
}

render() {
const { onDelete } = this.props
const { onDelete, disabled } = this.props
const { open } = this.state

return (
Expand All @@ -45,6 +45,7 @@ class MilestoneDeleteButton extends React.Component {
type="button"
className="tc-btn tc-btn-link"
styleName="icon-button"
disabled={disabled}
onClick={(event) => {
event.stopPropagation()

Expand Down Expand Up @@ -84,6 +85,7 @@ class MilestoneDeleteButton extends React.Component {

MilestoneDeleteButton.propTypes = {
onDelete: PT.func,
disabled: PT.bool
}

export default MilestoneDeleteButton
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ function MilestoneRow({
isCreatingRow,
isUpdatable,
phaseMembers,
disableDeleteAction
}) {
const phaseStatusOptions = PHASE_STATUS_OPTIONS
const edit = milestone.edit
Expand Down Expand Up @@ -294,6 +295,7 @@ function MilestoneRow({
<IconPencil />
</button>
<MilestoneDeleteButton
disabled={disableDeleteAction}
onDelete={() => {
onRemove(milestone.id)
}}
Expand Down Expand Up @@ -322,6 +324,7 @@ MilestoneRow.propTypes = {
allMilestones: PT.arrayOf(PT.shape()),
isCreatingRow: PT.bool,
isUpdatable: PT.bool,
disableDeleteAction: PT.bool,
members: PT.object,
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,17 @@
> *:not(:last-child) {
margin-right: 10px;
}

button {
color: black;
}
svg {
width: 14px;
height: 14px;

path {
fill: currentColor
}
}
}
}