diff --git a/src/api/projectMemberInvites.js b/src/api/projectMemberInvites.js
index f6250d44a..660c0c8bc 100644
--- a/src/api/projectMemberInvites.js
+++ b/src/api/projectMemberInvites.js
@@ -22,7 +22,8 @@ export function updateProjectMemberInvite(projectId, member) {
* @return {object} project member invite returned by api
*/
export function createProjectMemberInvite(projectId, member) {
- const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/invite/`
+ const fields = 'id,projectId,userId,email,role,status,createdAt,updatedAt,createdBy,updatedBy,handle,firstName,lastName,photoURL'
+ const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/invite/?fields=` + encodeURIComponent(fields)
return axios({
method: 'post',
url,
@@ -38,6 +39,16 @@ export function createProjectMemberInvite(projectId, member) {
})
}
+export function getProjectMemberInvites(projectId) {
+ const fields = 'id,projectId,userId,email,role,status,createdAt,updatedAt,createdBy,updatedBy,handle,firstName,lastName,photoURL'
+ const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/invites/?fields=`
+ + encodeURIComponent(fields)
+ return axios.get(url)
+ .then( resp => {
+ return resp.data.result.content
+ })
+}
+
/**
* Get a project member invite based on project's id
* @param {integer} projectId unique identifier of the project
diff --git a/src/api/projectMembers.js b/src/api/projectMembers.js
index 9c1bbc0ca..9eed57488 100644
--- a/src/api/projectMembers.js
+++ b/src/api/projectMembers.js
@@ -51,7 +51,9 @@ export function addProjectMember(projectId, newMember) {
export function updateProjectMember(projectId, memberId, updatedProps) {
- const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/${memberId}/`
+ const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,photoURL,workingHourStart,workingHourEnd,timeZone'
+ const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/${memberId}/?fields=`
+ + encodeURIComponent(fields)
return axios.patch(url, { param: updatedProps })
.then(resp => {
return resp.data.result.content
@@ -66,3 +68,23 @@ export function removeProjectMember(projectId, memberId) {
return memberId
})
}
+
+export function getProjectMembers(projectId) {
+ const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,photoURL,workingHourStart,workingHourEnd,timeZone'
+ const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/?fields=`
+ + encodeURIComponent(fields)
+ return axios.get(url)
+ .then( resp => {
+ return resp.data.result.content
+ })
+}
+
+export function getProjectMember(projectId, memberId) {
+ const fields = 'id,userId,role,isPrimary,deletedAt,createdAt,updatedAt,deletedBy,createdBy,updatedBy,handle,firstName,lastName,photoURL,workingHourStart,workingHourEnd,timeZone'
+ const url = `${PROJECTS_API_URL}/v4/projects/${projectId}/members/${memberId}?fields=`
+ + encodeURIComponent(fields)
+ return axios.get(url)
+ .then( resp => {
+ return resp.data.result.content
+ })
+}
\ No newline at end of file
diff --git a/src/api/templates.js b/src/api/templates.js
index e883989bb..a5e87ea6e 100644
--- a/src/api/templates.js
+++ b/src/api/templates.js
@@ -93,13 +93,19 @@ export function updateProjectsMetadata(metadataId, type, data) {
return axios.patch(`${PROJECTS_API_URL}/v4/projects/metadata/${type}/${key}/versions/${version}`, {
param: tmpdata
})
- .then(resp => _.get(resp.data, 'result.content', {}))
+ .then(resp => {
+ const versionMetadata = _.get(resp.data, 'result.content', {})
+ return { type, versionMetadata }
+ })
} else {
const path = type !== 'milestoneTemplates' ? 'projects' : 'timelines'
return axios.patch(`${PROJECTS_API_URL}/v4/${path}/metadata/${type}/${metadataId}`, {
param: data
})
- .then(resp => _.get(resp.data, 'result.content', {}))
+ .then(resp => {
+ const metadata = _.get(resp.data, 'result.content', {})
+ return { type, metadata }
+ })
}
}
diff --git a/src/assets/icons/daylight.svg b/src/assets/icons/daylight.svg
new file mode 100644
index 000000000..f2f3a5171
--- /dev/null
+++ b/src/assets/icons/daylight.svg
@@ -0,0 +1,13 @@
+
+
+
+ daylight
+ Created with Sketch.
+
+
+
+
\ No newline at end of file
diff --git a/src/assets/icons/moon.svg b/src/assets/icons/moon.svg
new file mode 100644
index 000000000..12cc3e46f
--- /dev/null
+++ b/src/assets/icons/moon.svg
@@ -0,0 +1,13 @@
+
+
+
+ moon
+ Created with Sketch.
+
+
+
+
\ No newline at end of file
diff --git a/src/components/AuthenticatedComponent.jsx b/src/components/AuthenticatedComponent.jsx
index 620b27f0c..bc615d97a 100644
--- a/src/components/AuthenticatedComponent.jsx
+++ b/src/components/AuthenticatedComponent.jsx
@@ -22,8 +22,8 @@ export function requiresAuthentication(Component) {
this.setState({isLoggedIn: true})
}).catch((error) => {
console.log(error)
- // FIXME should we include hash, search etc
- const redirectBackToUrl = window.location.origin + this.props.location.pathname
+ // we have to to redirect to the same page, so we use the whole URL
+ const redirectBackToUrl = encodeURIComponent(window.location.href)
const newLocation = ACCOUNTS_APP_LOGIN_URL + '?retUrl=' + redirectBackToUrl
console.log('redirecting... ', newLocation)
window.location = newLocation
diff --git a/src/components/ProjectStatus/editableProjectStatus.js b/src/components/ProjectStatus/editableProjectStatus.js
index 339956bed..68347116b 100644
--- a/src/components/ProjectStatus/editableProjectStatus.js
+++ b/src/components/ProjectStatus/editableProjectStatus.js
@@ -10,7 +10,8 @@ import {
PROJECT_STATUS_ACTIVE,
PROJECT_STATUS_COMPLETED,
PROJECT_STATUS_CANCELLED,
- TOOLTIP_DEFAULT_DELAY
+ TOOLTIP_DEFAULT_DELAY,
+ PROJECT_STATUS_DRAFT
} from '../../config/constants'
import CarretDownNormal9px from '../../assets/icons/arrow-9px-carret-down-normal.svg'
@@ -63,7 +64,7 @@ const hocStatusDropdown = (CompositeComponent, statusList, projectCanBeActive) =
Project Status
{
- activestatusList.sort((a, b) => a.dropDownOrder > b.dropDownOrder).map((item) =>
+ activestatusList.sort((a, b) => a.order - b.order).map((item) =>
item.toolTipMessage ? (
@@ -121,6 +122,7 @@ const editableProjectStatus = (CompositeComponent) => class extends Component {
this.showStatusChangeDialog = this.showStatusChangeDialog.bind(this)
this.changeStatus = this.changeStatus.bind(this)
this.handleReasonUpdate = this.handleReasonUpdate.bind(this)
+ this.getProjectStatusDropdownValues = this.getProjectStatusDropdownValues.bind(this)
}
componentWillReceiveProps() {
@@ -158,12 +160,20 @@ const editableProjectStatus = (CompositeComponent) => class extends Component {
this.setState({ statusChangeReason : _.get(reason, 'value') })
}
+ getProjectStatusDropdownValues(status) {
+ if (status === PROJECT_STATUS_DRAFT) {
+ return [{color: 'gray', name: 'Draft', fullName: 'Project is in draft', value: PROJECT_STATUS_DRAFT, order: 2, dropDownOrder: 1 }].concat(PROJECT_STATUS)
+ }
+ return PROJECT_STATUS
+ }
+
render() {
const { showStatusChangeDialog, newStatus, statusChangeReason } = this.state
- const { canEdit, projectCanBeActive } = this.props
+ const { canEdit, projectCanBeActive, status } = this.props
+ const PROJECT_STATUS_VALUES = this.getProjectStatusDropdownValues(status)
const StatusDropdown = canEdit
- ? enhanceDropdown(hocStatusDropdown(CompositeComponent, PROJECT_STATUS, projectCanBeActive))
- : hocStatusDropdown(CompositeComponent, PROJECT_STATUS, projectCanBeActive)
+ ? enhanceDropdown(hocStatusDropdown(CompositeComponent, PROJECT_STATUS_VALUES, projectCanBeActive))
+ : hocStatusDropdown(CompositeComponent, PROJECT_STATUS_VALUES, projectCanBeActive)
return (
@@ -190,7 +200,11 @@ editableProjectStatus.propTypes = {
/**
* Boolean flag to control if project status can be switched to active.
*/
- projectCanBeActive: PropTypes.bool
+ projectCanBeActive: PropTypes.bool,
+ /**
+ * String representing project status
+ */
+ status: PropTypes.string
}
export default editableProjectStatus
diff --git a/src/components/Sticky.jsx b/src/components/Sticky.jsx
index 8100feb35..d42dba56f 100644
--- a/src/components/Sticky.jsx
+++ b/src/components/Sticky.jsx
@@ -7,14 +7,26 @@ export default class Sticky extends React.Component {
super(props)
this.mountSticky = (sticky) => { this.sticky = sticky }
this.handleScroll = this.handleScroll.bind(this)
+ this.updateSticky = this.updateSticky.bind(this)
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll)
+ document.addEventListener('refreshsticky', this.updateSticky)
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll)
+ document.removeEventListener('refreshsticky', this.updateSticky)
+ }
+
+ updateSticky() {
+ setTimeout(() => {
+ if (this.sticky) {
+ this.sticky.updateInitialDimension()
+ this.sticky.update()
+ }
+ })
}
handleScroll() {
diff --git a/src/components/TeamManagement/MemberItem.jsx b/src/components/TeamManagement/MemberItem.jsx
new file mode 100644
index 000000000..2c96ab976
--- /dev/null
+++ b/src/components/TeamManagement/MemberItem.jsx
@@ -0,0 +1,88 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import moment from 'moment'
+import {timezones} from 'appirio-tech-react-components/constants/timezones'
+import UserTooltip from '../User/UserTooltip'
+import SunIcon from '../../assets/icons/daylight.svg'
+import MoonIcon from '../../assets/icons/moon.svg'
+import { getFullNameWithFallback } from '../../helpers/tcHelpers'
+import './MemberItem.scss'
+
+const MemberItem = (props) => {
+
+ const {usr, showEmailOnly} = props
+
+ const userFullName = getFullNameWithFallback(usr)
+ const workingHourStart = _.get(usr, 'workingHourStart')
+ const workingHourEnd = _.get(usr, 'workingHourEnd')
+ const timeZone = _.get(usr, 'timeZone')
+ const email = _.get(usr, 'email')
+ let localTime
+ let timeZoneInfo
+ if(timeZone) {
+ timeZoneInfo = _.find(timezones, (t) => {return t.zoneName === timeZone})
+ localTime = moment().utcOffset(timeZoneInfo.gmtOffset/3600).format('h:mm A Z')
+ }
+ let localTimeInfoEl = null
+
+ let isWorkingTime = false
+ let showIcon = false
+ let localWhStart
+ let localWhEnd
+
+ if(localTime || workingHourStart && workingHourEnd ) {
+
+ if(workingHourStart && workingHourEnd) {
+ showIcon = true
+ localWhStart = moment({hour: workingHourStart.split(':')[0]}).format('h:mm A')
+ localWhEnd = moment({hour: workingHourEnd.split(':')[0]}).format('h:mm A')
+
+ if(localTime) {
+ let localHour = +moment().utcOffset(timeZoneInfo.gmtOffset/3600).format('H')
+ const localStartHour = +moment({hour: workingHourStart.split(':')[0] }).format('H')
+ let localEndHour = +moment({hour: workingHourEnd.split(':')[0] }).format('H')
+ if(localEndHour <= localStartHour) {
+ localEndHour += 24
+ if(localHour < localStartHour) {
+ localHour += 24
+ }
+ }
+ if(localHour >= localStartHour && localHour <= localEndHour) {
+ isWorkingTime = true
+ }
+ }
+ }
+ localTimeInfoEl = (
+ {localTime? Local Time - {localTime} : null}
+ {localWhStart && localWhEnd ? Working Hours - {`${localWhStart} - ${localWhEnd}`} : null}
+
)
+ }
+
+ return (
+
+
+
+
{showEmailOnly? email :userFullName}
+ {localWhStart && localWhEnd &&
WH: {localWhStart} - {localWhEnd}
}
+ {localTime &&
{showIcon&& (isWorkingTime ? : )}Local time: {localTime}
}
+
+
+ )
+}
+
+MemberItem.propTypes = {
+ showRoleSelector: false
+}
+
+MemberItem.propTypes = {
+ usr: PropTypes.object.isRequired,
+ id: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number
+ ]).isRequired,
+ previewAvatar: PropTypes.bool,
+ showEmailOnly: PropTypes.bool,
+ size: PropTypes.number
+}
+
+export default MemberItem
diff --git a/src/components/TeamManagement/MemberItem.scss b/src/components/TeamManagement/MemberItem.scss
new file mode 100644
index 000000000..f2d6ab976
--- /dev/null
+++ b/src/components/TeamManagement/MemberItem.scss
@@ -0,0 +1,69 @@
+@import '~tc-ui/src/styles/tc-includes';
+
+.container {
+ display: flex;
+ flex-direction: row;
+ min-height: 51px;
+ width: 100%;
+
+ & + & {
+ margin-top: 2 * $base-unit;
+ }
+
+ :global(.Tooltip) {
+ margin-left: 5px;
+ margin-top: 2px;
+
+ &:global(.customer-data .tooltip-content-container){
+ width: 440px;
+ }
+ }
+}
+
+.member-detail {
+ margin-left: 2 * $base-unit;
+ overflow: hidden;
+}
+
+.member-name {
+ @include roboto-bold;
+ font-size: 16px;
+ line-height: 20px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.wk-hour {
+ margin-top: $base-unit;
+ font-size: 10px;
+ color: $tc-gray-50;
+}
+
+.local-time {
+ align-items: center;
+ display: flex;
+ font-size: 10px;
+ color: $tc-gray-50;
+ line-height: 20px;
+
+ svg {
+ vertical-align: sub;
+ margin-right: $base-unit;
+ }
+}
+
+.time-info-tooltip {
+ height: 20px;
+ width: 100%;
+ color: $tc-gray-50;
+ display: flex;
+ justify-content: space-between;
+
+ span {
+ font-size: 12px;
+ color: $tc-gray-50;
+ line-height: 20px;
+ white-space: nowrap
+ }
+}
diff --git a/src/components/TeamManagement/ProjectManagementDialog.js b/src/components/TeamManagement/ProjectManagementDialog.js
index aa57bbf7d..ea4a7c8be 100644
--- a/src/components/TeamManagement/ProjectManagementDialog.js
+++ b/src/components/TeamManagement/ProjectManagementDialog.js
@@ -135,7 +135,7 @@ class ProjectManagementDialog extends React.Component {
@@ -156,23 +156,24 @@ class ProjectManagementDialog extends React.Component {
removeInvite(invite)
}
i++
- const handle = invite.member ? invite.member.handle : null
- const userFullName = getFullNameWithFallback(invite.member)
+ const hasUserId = !_.isNil(invite.userId)
+ const handle = invite.handle
+ const userFullName = getFullNameWithFallback(invite)
return (
- {!invite.email && {userFullName} }
+ {hasUserId && {userFullName} }
- {!invite.email && @{handle} }
- {invite.email && {invite.email} }
+ {hasUserId && handle && @{handle} }
+ { (!hasUserId) && {invite.email} }
{showRemove &&
diff --git a/src/components/TeamManagement/TeamManagement.jsx b/src/components/TeamManagement/TeamManagement.jsx
index 8fb91eed6..5bda77f04 100644
--- a/src/components/TeamManagement/TeamManagement.jsx
+++ b/src/components/TeamManagement/TeamManagement.jsx
@@ -4,7 +4,7 @@ import uncontrollable from 'uncontrollable'
import './TeamManagement.scss'
import ProjectDialog from './ProjectManagementDialog'
import TopcoderDialog from './TopcoderManagementDialog'
-import UserTooltip from '../User/UserTooltip'
+import MemberItem from './MemberItem'
import AddIcon from '../../assets/icons/icon-ui-bold-add.svg'
import Dialog from './Dialog'
import PERMISSIONS from '../../config/permissions'
@@ -26,8 +26,8 @@ const REMOVE_INVITATION_TITLE = 'You\'re about to remove an invitation'
const REMOVE_TITLE = 'You\'re about to delete a member from the team'
const LEAVE_TITLE = 'You\'re about to leave the project'
-const LEAVE_MESSAGE = `Once you leave, somebody on your team has to add you for you to be
- for you to be able to see the project. Do you stil want to leave?`
+const LEAVE_MESSAGE = `Once you leave, somebody on your team has to add you back to the team for you to be able to see the project.
+ Do you still want to leave?`
const JOIN_MESSAGE = `You are about to join the project. Once you join you will be responsible for project delivery.
Are you sure you want to join?`
@@ -41,6 +41,31 @@ const REMOVE_INVITE_MESSAGE = `Once you cancel the invitation for
Team
+ {(customerTeamManageAction) &&
+ onShowProjectDialog(true)}>
+ Manage
+
+ }
{sortedMembers.map((member, i) => {
if (!member.isCustomer) {
return
}
+ projectTeamInviteCount++
+ if(!projectTeamInviteButtonExpanded && projectTeamInviteCount > 3) {
+ return null
+ }
return (
-
+
)
})}
{projectTeamInvites.map((invite, i) => {
- const member = invite.email ? { email: invite.email } : invite.member
-
+ // const member = invite.email ? { email: invite.email } : invite.member
+ projectTeamInviteCount++
+ if(!projectTeamInviteButtonExpanded && projectTeamInviteCount > 3) {
+ return null
+ }
return (
-
)
})}
+ {projectTeamInviteCount >3 &&
+
+
+ {!projectTeamInviteButtonExpanded ?'Show All': 'Show Less'}
+
+
+ }
{ (canShowInvite) &&
-
onShowProjectDialog(true)}>
-
- Manage Invitations
-
+
+
onShowProjectDialog(true)}>
+
+
+ Manage Invitations
+
+
+
}
@@ -126,29 +189,50 @@ class TeamManagement extends React.Component {
if (member.isCustomer) {
return
}
+
+ topcoderTeamInviteCount++
+ if(!topcoderTeamInviteButtonExpanded &&topcoderTeamInviteCount > 3) {
+ return null
+ }
+
return (
-
+
)
})}
{topcoderTeamInvites.map((invite, i) => {
if (invite.isCustomer) {
return
}
+ topcoderTeamInviteCount++
+ if(!topcoderTeamInviteButtonExpanded &&topcoderTeamInviteCount > 3) {
+ return null
+ }
+
return (
-
)
})}
+ {topcoderTeamInviteCount >3 &&
+
+
+ {!topcoderTeamInviteButtonExpanded ?'Show All': 'Show Less'}
+
+
+ }
{ (canJoinAsCopilot || canJoinAsManager) &&
- onJoin(true)}>
-
- {canJoinAsCopilot ? 'Join project as copilot' : 'Join project'}
+
+
onJoin(true)}>
+
+
+ {canJoinAsCopilot ? 'Join project as copilot' : 'Join project'}
+
+
}
@@ -216,6 +300,7 @@ class TeamManagement extends React.Component {
return (
r.value === member.role), 'title')
+ const isMemberProcessing = _.includes(updatingMemberIds, member.id)
return (
@@ -226,36 +230,39 @@ class TopcoderManagementDialog extends React.Component {
this.onUserRoleChange(member.userId, member.id, type)
}
return (
-
- {types.map((type) => {
- const isCopilotDisabled =
- type === 'Copilot' &&
- type !== currentType &&
- !(currentUser.isCopilotManager || currentUser.isAdmin)
-
- return (
- isCopilotDisabled ? (
-
-
-
+
+ {
+ isMemberProcessing ?
:
+ types.map((type) => {
+ const isCopilotDisabled =
+ type === 'Copilot' &&
+ type !== currentType &&
+ !(currentUser.isCopilotManager || currentUser.isAdmin)
+
+ return (
+ isCopilotDisabled ? (
+
+
+
+ {'Only Connect Copilot Managers can change member role to copilots.'}
+
+
+ ) : (
+
onClick(type)}
+ className={cn('member-role', { active: type === currentType })}
+ >
{type}
-
-
- {'Only Connect Copilot Managers can change member role to copilots.'}
-
-
- ) : (
-
onClick(type)}
- className={cn('member-role', { active: type === currentType })}
- >
- {type}
-
- )
- )
- })}
+ )
+ )
+ })
+ }
)
})()}
@@ -267,18 +274,24 @@ class TopcoderManagementDialog extends React.Component {
removeInvite(invite)
}
const approve = () => {
+ this.setState(prevState => ({ processingInviteRequestIds: [ ...prevState.processingInviteRequestIds, invite.id ] }))
approveOrDecline({
userId: invite.userId,
status: 'request_approved'
+ }).then(() => {
+ this.setState(prevState => ({ processingInviteRequestIds: _.xor(prevState.processingInviteRequestIds, [invite.id]) }))
})
}
const decline = () => {
+ this.setState(prevState => ({ processingInviteRequestIds: [ ...prevState.processingInviteRequestIds, invite.id ] }))
approveOrDecline({
userId: invite.userId,
status: 'request_rejected'
+ }).then(() => {
+ this.setState(prevState => ({ processingInviteRequestIds: _.xor(prevState.processingInviteRequestIds, [invite.id]) }))
})
}
- const userFullName = getFullNameWithFallback(invite.member)
+ const userFullName = getFullNameWithFallback(invite)
i++
return (
{userFullName}
- @{invite.member.handle || 'ConnectUser'}
+ @{invite.handle || 'ConnectUser'}
{
invite.status===PROJECT_MEMBER_INVITE_STATUS_REQUESTED && showApproveDecline &&
-
approve
-
decline
+ {!_.includes(processingInviteRequestIds, invite.id) ? ([
+
approve ,
+
decline
+ ]) : (
+
+ )}
Requested {moment(invite.createdAt).format('MMM D, YY')}
diff --git a/src/components/User/UserTooltip.jsx b/src/components/User/UserTooltip.jsx
index 2d6399f04..d2a49ab2d 100644
--- a/src/components/User/UserTooltip.jsx
+++ b/src/components/User/UserTooltip.jsx
@@ -7,13 +7,16 @@ import { DOMAIN } from '../../config/constants'
import { getAvatarResized, getFullNameWithFallback } from '../../helpers/tcHelpers'
import IconDirectArrow from '../../assets/icons/icon-direct-arrow.svg'
+//
require('./UserTooltip.scss')
-const UserTooltip = ({ usr, id, previewAvatar, size, invitedLabel, showEmailOnly }) => {
+const UserTooltip = ({ usr, id, previewAvatar, size, invitedLabel, showEmailOnly, localTimeInfo}) => {
const theme = `customer-data size-${size}`
const tooltipMargin = previewAvatar ? -(100 + (id * 20)) : 0
const userHandle = _.get(usr, 'handle')
const userEmail = _.get(usr, 'email')
+
+
const userFullName = getFullNameWithFallback(usr)
const avatar =
(
@@ -61,6 +64,7 @@ const UserTooltip = ({ usr, id, previewAvatar, size, invitedLabel, showEmailOnly
{userEmail}
}
{invitedLabel &&
invited
}
+ {localTimeInfo}
@@ -75,6 +79,7 @@ UserTooltip.propTypes = {
PropTypes.number
]).isRequired,
previewAvatar: PropTypes.bool,
+ localTimeInfo: PropTypes.element,
size: PropTypes.number
}
diff --git a/src/components/User/UserTooltip.scss b/src/components/User/UserTooltip.scss
index deae45b05..e94fcb192 100644
--- a/src/components/User/UserTooltip.scss
+++ b/src/components/User/UserTooltip.scss
@@ -3,6 +3,11 @@
.Tooltip {
&.customer-data {
.tooltip-target {
+ display: flex;
+ &>div:first-child {
+ margin-left: -5px;
+ position: relative;
+ }
position: relative;
.Avatar {
height: 30px;
@@ -215,6 +220,7 @@
height: 40px;
}
+
.invited-label {
@include roboto-medium;
background-color: $tc-gray-05;
diff --git a/src/config/constants.js b/src/config/constants.js
index c9bafcd92..e511daee2 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -219,10 +219,10 @@ export const LOAD_PROJECT_PENDING = 'LOAD_PROJECT_PENDING'
export const LOAD_PROJECT_FAILURE = 'LOAD_PROJECT_FAILURE'
export const LOAD_PROJECT_SUCCESS = 'LOAD_PROJECT_SUCCESS'
-export const LOAD_PROJECT_MEMBER_INVITES = 'LOAD_PROJECT_MEMBER_INVITES'
-export const LOAD_PROJECT_MEMBER_INVITES_PENDING = 'LOAD_PROJECT_MEMBER_INVITES_PENDING'
-export const LOAD_PROJECT_MEMBER_INVITES_FAILURE = 'LOAD_PROJECT_MEMBER_INVITES_FAILURE'
-export const LOAD_PROJECT_MEMBER_INVITES_SUCCESS = 'LOAD_PROJECT_MEMBER_INVITES_SUCCESS'
+export const LOAD_PROJECT_MEMBER_INVITE = 'LOAD_PROJECT_MEMBER_INVITE'
+export const LOAD_PROJECT_MEMBER_INVITE_PENDING = 'LOAD_PROJECT_MEMBER_INVITE_PENDING'
+export const LOAD_PROJECT_MEMBER_INVITE_FAILURE = 'LOAD_PROJECT_MEMBER_INVITE_FAILURE'
+export const LOAD_PROJECT_MEMBER_INVITE_SUCCESS = 'LOAD_PROJECT_MEMBER_INVITE_SUCCESS'
export const CREATE_PROJECT = 'CREATE_PROJECT'
export const CREATE_PROJECT_PENDING = 'CREATE_PROJECT_PENDING'
@@ -386,6 +386,20 @@ export const UPDATE_PROJECT_MEMBER_FAILURE = 'UPDATE_PROJECT_MEMBER_FAILURE'
export const CLEAR_LOADED_PROJECT = 'CLEAR_LOADED_PROJECT'
+export const LOAD_PROJECT_MEMBERS = 'LOAD_PROJECT_MEMBERS'
+export const LOAD_PROJECT_MEMBERS_PENDING = 'LOAD_PROJECT_MEMBERS_PENDING'
+export const LOAD_PROJECT_MEMBERS_SUCCESS = 'LOAD_PROJECT_MEMBERS_SUCCESS'
+export const LOAD_PROJECT_MEMBERS_FAILURE = 'LOAD_PROJECT_MEMBERS_FAILURE'
+
+export const LOAD_PROJECT_MEMBER_INVITES = 'LOAD_PROJECT_MEMBER_INVITES'
+export const LOAD_PROJECT_MEMBER_INVITES_PENDING = 'LOAD_PROJECT_MEMBER_INVITES_PENDING'
+export const LOAD_PROJECT_MEMBER_INVITES_SUCCESS = 'LOAD_PROJECT_MEMBER_INVITES_SUCCESS'
+export const LOAD_PROJECT_MEMBER_INVITES_FAILURE = 'LOAD_PROJECT_MEMBER_INVITES_FAILURE'
+
+export const LOAD_PROJECT_MEMBER = 'LOAD_PROJECT_MEMBER'
+export const LOAD_PROJECT_MEMBER_PENDING = 'LOAD_PROJECT_MEMBER_PENDING'
+export const LOAD_PROJECT_MEMBER_SUCCESS = 'LOAD_PROJECT_MEMBER_SUCCESS'
+export const LOAD_PROJECT_MEMBER_FAILURE = 'LOAD_PROJECT_MEMBER_FAILURE'
// Project attachments
export const ADD_PROJECT_ATTACHMENT = 'ADD_PROJECT_ATTACHMENT'
@@ -566,7 +580,7 @@ export const PHASE_STATUS_CANCELLED = 'cancelled'
export const PHASE_STATUS_PAUSED = 'paused'
export const PROJECT_STATUS = [
- {color: 'gray', name: 'Draft', fullName: 'Project is in draft', value: PROJECT_STATUS_DRAFT, order: 2, dropDownOrder: 1 },
+ // {color: 'gray', name: 'Draft', fullName: 'Project is in draft', value: PROJECT_STATUS_DRAFT, order: 2, dropDownOrder: 1 },
{color: 'gray', name: 'In review', fullName: 'Project is in review', value: PROJECT_STATUS_IN_REVIEW, order: 3, dropDownOrder: 2 },
{color: 'gray', name: 'Reviewed', fullName: 'Project is reviewed', value: PROJECT_STATUS_REVIEWED, order: 4, dropDownOrder: 3 },
{color: 'green', name: 'Active', fullName: 'Project is active', value: PROJECT_STATUS_ACTIVE, order: 1, dropDownOrder: 4 },
@@ -737,6 +751,8 @@ export const LS_INCOMPLETE_PROJECT_QUERY_PARAMS = 'incompleteProjectQueryParams'
export const SPECIAL_QUERY_PARAMS = ['returnUrl', 'refCode']
export const PROJECTS_API_URL = process.env.PROJECTS_API_URL || TC_API_URL
+// for local testing Connect App with Project Service, comment the previous line and uncomment the next one
+// export const PROJECTS_API_URL = 'http://localhost:8001'
export const CONNECT_MESSAGE_API_URL = process.env.CONNECT_MESSAGE_API_URL || TC_API_URL
export const NEW_PROJECT_PATH = '/new-project'
diff --git a/src/config/projectWizard/index.js b/src/config/projectWizard/index.js
index 50841339d..632252241 100644
--- a/src/config/projectWizard/index.js
+++ b/src/config/projectWizard/index.js
@@ -580,13 +580,13 @@ export function getProductEstimate(projectTemplate, projectData) {
let minTime = 0
let maxTime = 0
let matchedBlocks = []
- const flatProjectData = flatten(removeValuesOfHiddenNodes(projectTemplate, projectData), { safe: true })
if (projectTemplate) {
const sections = _.get(projectTemplate, 'scope.sections')
const addonPriceConfig = _.get(projectTemplate, 'scope.addonPriceConfig', {})
const priceConfig = _.get(projectTemplate, 'scope.priceConfig', {})
const buildingBlocks = _.get(projectTemplate, 'scope.buildingBlocks', {})
const preparedConditions = _.cloneDeep(_.get(projectTemplate, 'scope.preparedConditions', {}))
+ const flatProjectData = flatten(removeValuesOfHiddenNodes(projectTemplate.scope, projectData), { safe: true })
_.forOwn(preparedConditions, (cond, placeholder) => {
preparedConditions[placeholder] = evaluate(cond, flatProjectData) === true ? '1 == 1' : '1 == 2'
})
diff --git a/src/helpers/wizardHelper.js b/src/helpers/wizardHelper.js
index 882f53f7f..8f5e7ac4e 100644
--- a/src/helpers/wizardHelper.js
+++ b/src/helpers/wizardHelper.js
@@ -1073,7 +1073,7 @@ export const removeValuesOfHiddenNodes = (template, project) => {
// if some option is hidden, we remove it's value from the list of values of the parent question
case LEVEL.OPTION: {
- if (_.get(nodeObject, 'nodeObject.__wizard.hiddenByCondition')) {
+ if (_.get(nodeObject, '__wizard.hiddenByCondition')) {
const questionNode = {...node, optionIndex: -1}
const questionNodeObject = getNodeObject(template, questionNode)
const questionValue = _.get(updatedProject, questionNodeObject.fieldName)
diff --git a/src/index.html b/src/index.html
index c727369d3..01e9dd83b 100644
--- a/src/index.html
+++ b/src/index.html
@@ -10,7 +10,7 @@
-