Skip to content
Merged

Cf18 #3168

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
aa5d877
issue 3104 fix
rashmi73 Jul 4, 2019
d2de518
issues-3130: Clear error message in delete
nahidshahin Jul 4, 2019
db23a97
Fix template reducer to show error message when failed on creating ne…
gets0ul Jul 4, 2019
25bd2df
fix for issue-3059
sumitdaga Jul 4, 2019
0de0390
issue 3126 fix
PrakashDurlabhji Jul 4, 2019
726333a
Update projects.js
PrakashDurlabhji Jul 4, 2019
0f5938e
Merge pull request #3137 from rashmi73/issue_3104
maxceem Jul 5, 2019
2e40164
Merge pull request #3138 from nahidshahin/issues_3130
maxceem Jul 5, 2019
ab4ed91
Merge pull request #3141 from sumitdaga/issue-3059
maxceem Jul 5, 2019
380770c
fix for issue 3147
sumitdaga Jul 5, 2019
615536e
issue 3051 fix
PrakashDurlabhji Jul 6, 2019
de81d1d
Merge pull request #3140 from gets0ul/issue-3112
maxceem Jul 6, 2019
d8b1653
Merge pull request #3143 from PrakashDurlabhji/issue_3126
maxceem Jul 6, 2019
51f8078
Merge pull request #3144 from PrakashDurlabhji/issue_3088
maxceem Jul 6, 2019
b1322b2
issue 3051 fix
PrakashDurlabhji Jul 7, 2019
7c7db40
Make textarea can expand manually in Scope form
mfikria Jul 8, 2019
a602aa6
add textarea rows 3
mfikria Jul 8, 2019
1751bbe
Merge pull request #3148 from sumitdaga/issue-3147
maxceem Jul 9, 2019
547f792
make tooltip text multiline for accordions, so long text is not cut o…
maxceem Jul 9, 2019
bc18cac
issue 3051 stateful notificationdropdwn
PrakashDurlabhji Jul 9, 2019
2d61c82
3051 final fix
PrakashDurlabhji Jul 10, 2019
f8b3240
Merge pull request #3139 from mfikria/cf18
maxceem Jul 11, 2019
4ce12b5
3051 redudant code removed
PrakashDurlabhji Jul 11, 2019
5d9c1b2
minor fix
PrakashDurlabhji Jul 11, 2019
6ad523d
3051 warning removed
PrakashDurlabhji Jul 12, 2019
c05a780
3051 redundancy removed
PrakashDurlabhji Jul 12, 2019
fd57215
Merge pull request #3153 from PrakashDurlabhji/issue_3051
maxceem Jul 12, 2019
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
2 changes: 1 addition & 1 deletion src/api/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TC_API_URL, PROJECTS_API_URL, PROJECTS_LIST_PER_PAGE } from '../config/

export function getProjects(criteria, pageNum) {
// add default params
const includeFields = ['id', 'name', 'description', 'members', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version']
const includeFields = ['id', 'name', 'description', 'members', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version', 'templateId']
const params = {
limit: PROJECTS_LIST_PER_PAGE,
offset: (pageNum - 1) * PROJECTS_LIST_PER_PAGE,
Expand Down
59 changes: 41 additions & 18 deletions src/components/NotificationsDropdown/NotificationsDropdown.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,48 @@ import EnhancedDropdown from './EnhancedDropdown'
import NotificationsBell from './NotificationsBell'


const NotificationsDropdown = (props) => {
return (
<div className="notifications-dropdown">
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={props.onToggle}>
<div className="dropdown-menu-header">
<NotificationsBell
hasUnread={props.hasUnread}
hasNew={props.hasNew}
onClick={props.onToggle}
/>
</div>
<div className="dropdown-menu-list">
<div className="notifications-dropdown-content">
{props.children}
class NotificationsDropdown extends React.Component {

constructor(props) {
super(props)
this.state = {
isOpen: false
}

this.toggle = this.toggle.bind(this)
}

toggle(isOpen) {
if (typeof isOpen === 'object') {
this.props.onToggle(!this.state.isOpen)
this.setState({ isOpen: !this.state.isOpen})
} else {
this.props.onToggle(isOpen)
this.setState({ isOpen })
}
}

render() {
const { hasUnread, hasNew, children } = this.props
return (
<div className="notifications-dropdown">
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={this.toggle}>
<div className="dropdown-menu-header">
<NotificationsBell
hasUnread={hasUnread}
hasNew={hasNew}
onClick={this.toggle}
/>
</div>
<div className="dropdown-menu-list">
<div className="notifications-dropdown-content">
{children}
</div>
</div>
</div>
</EnhancedDropdown>
</div>
)
</EnhancedDropdown>
</div>
)
}
}

NotificationsDropdown.propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import _ from 'lodash'
import { TransitionGroup, Transition } from 'react-transition-group'
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, toggleNotificationRead, visitNotifications,
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, markAllNotificationsSeen, toggleNotificationRead,
toggleBundledNotificationRead, viewOlderNotifications, hideOlderNotifications } from '../../routes/notifications/actions'
import {
splitNotificationsBySources,
filterReadNotifications,
filterSeenNotifications,
limitQuantityInSources,
preRenderNotifications,
} from '../../routes/notifications/helpers/notifications'
Expand All @@ -29,9 +30,9 @@ import { NOTIFICATIONS_DROPDOWN_PER_SOURCE, NOTIFICATIONS_NEW_PER_SOURCE, REFRES
import './NotificationsDropdown.scss'

const NotificationsDropdownContainerView = (props) => {
const {initialized, isLoading, lastVisited, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
pending, toggleBundledNotificationRead, visitNotifications, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb } = props
const {initialized, isLoading, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
pending, toggleBundledNotificationRead, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb, markAllNotificationsSeen } = props
if (!initialized && isLoading) {
return (
<NotificationsDropdown hasUnread={false}>
Expand All @@ -51,9 +52,11 @@ const NotificationsDropdownContainerView = (props) => {
}

const notReadNotifications = filterReadNotifications(notifications)
const notSeenNotifications = filterSeenNotifications(notifications)
const allNotificationsBySources = splitNotificationsBySources(sources, notReadNotifications)

const hasUnread = notReadNotifications.length > 0
const hasUnseen = notSeenNotifications.length > 0
// we have to give Dropdown component some time
// before removing notification item node from the list
// otherwise dropdown thinks we clicked outside and closes dropdown
Expand All @@ -70,7 +73,7 @@ const NotificationsDropdownContainerView = (props) => {
}, 0)
}
}
const hasNew = hasUnread && lastVisited < _.maxBy(_.map(notifications, n => new Date(n.date)))

let notificationsEmpty = (
<NotificationsEmpty>
<p className="notifications-empty-note">
Expand All @@ -92,6 +95,12 @@ const NotificationsDropdownContainerView = (props) => {
)
}

const markNotificationsSeen = (isOpen) => {
if (isOpen) {
markAllNotificationsSeen(null, notifications)
}
}

// this function checks that notification is not seen yet,
// before marking it as seen
const markNotificationSeen = (notificationId) => {
Expand All @@ -118,10 +127,10 @@ const NotificationsDropdownContainerView = (props) => {
return (
<NotificationsDropdown
hasUnread={hasUnread}
hasNew={hasNew}
hasNew={hasUnseen}
onToggle={(isOpen) => {
toggleNotificationsDropdownWeb(isOpen)
visitNotifications()
markNotificationsSeen(isOpen)
}}
>
{isDropdownWebOpen && <div>
Expand Down Expand Up @@ -192,10 +201,9 @@ const NotificationsDropdownContainerView = (props) => {
return (
<NotificationsMobilePage
hasUnread={hasUnread}
hasNew={hasNew}
hasNew={hasUnseen}
onToggle={() => {
toggleNotificationsDropdownMobile()
visitNotifications()
}}
isOpen={isDropdownMobileOpen}
>
Expand Down Expand Up @@ -253,21 +261,18 @@ class NotificationsDropdownContainer extends React.Component {
constructor(props) {
super(props)
this.state = {
lastVisited: new Date(0),
isDropdownWebOpen: false,
isDropdownMobileOpen: false,
notificationsVisited: false,
}

this.onToggleNotificationsDropdownWeb = this.onToggleNotificationsDropdownWeb.bind(this)
this.onToggleNotificationsDropdownMobile = this.onToggleNotificationsDropdownMobile.bind(this)
this.onVisitNotifications = this.onVisitNotifications.bind(this)
}

componentDidMount() {
this.props.getNotifications()
this.autoRefreshNotifications = setInterval(() => this.props.getNotifications(), REFRESH_NOTIFICATIONS_INTERVAL)
this.setState({ lastVisited: this.props.lastVisited })
}

componentWillUnmount() {
Expand All @@ -276,7 +281,6 @@ class NotificationsDropdownContainer extends React.Component {
this.onToggleNotificationsDropdownMobile(false)
this.onToggleNotificationsDropdownWeb(false)
this.props.hideOlderNotifications()
this.state.notificationsVisited && this.props.visitNotifications()
}

componentWillReceiveProps(nextProps) {
Expand All @@ -300,25 +304,15 @@ class NotificationsDropdownContainer extends React.Component {
this.setState({ isDropdownMobileOpen: !_.isUndefined(isOpen) ? isOpen : !this.state.isDropdownMobileOpen})
}

onVisitNotifications() {
this.setState({
lastVisited: _.maxBy(_.map(this.props.notifications, n => new Date(n.date))),
notificationsVisited: true
})
}

render() {
const { notifications, ...restProps } = this.props
const preRenderedNotifications = preRenderNotifications(notifications)

return (
<NotificationsDropdownContainerView
{...restProps}
notifications={preRenderedNotifications}
toggleNotificationsDropdownWeb={this.onToggleNotificationsDropdownWeb}
toggleNotificationsDropdownMobile={this.onToggleNotificationsDropdownMobile}
visitNotifications={this.onVisitNotifications}
lastVisited={this.state.lastVisited}
isDropdownMobileOpen={this.state.isDropdownMobileOpen}
isDropdownWebOpen={this.state.isDropdownWebOpen}
/>
Expand All @@ -331,9 +325,9 @@ const mapStateToProps = ({ notifications }) => notifications

const mapDispatchToProps = {
getNotifications,
visitNotifications,
toggleNotificationSeen,
markAllNotificationsRead,
markAllNotificationsSeen,
toggleNotificationRead,
toggleBundledNotificationRead,
viewOlderNotifications,
Expand Down
2 changes: 1 addition & 1 deletion src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ export const LOAD_ORG_CONFIG_FAILURE = 'LOAD_ORG_CONFIG_FAILURE'
export const GET_NOTIFICATIONS_PENDING = 'GET_NOTIFICATIONS_PENDING'
export const GET_NOTIFICATIONS_SUCCESS = 'GET_NOTIFICATIONS_SUCCESS'
export const GET_NOTIFICATIONS_FAILURE = 'GET_NOTIFICATIONS_FAILURE'
export const VISIT_NOTIFICATIONS = 'VISIT_NOTIFICATIONS'
export const SET_NOTIFICATIONS_FILTER_BY = 'SET_NOTIFICATIONS_FILTER_BY'
export const MARK_ALL_NOTIFICATIONS_READ = 'MARK_ALL_NOTIFICATIONS_READ'
export const TOGGLE_NOTIFICATION_READ = 'TOGGLE_NOTIFICATION_READ'
export const TOGGLE_NOTIFICATION_SEEN = 'TOGGLE_NOTIFICATION_SEEN'
export const MARK_ALL_NOTIFICATIONS_SEEN = 'MARK_ALL_NOTIFICATIONS_SEEN'
export const VIEW_OLDER_NOTIFICATIONS_SUCCESS = 'VIEW_OLDER_NOTIFICATIONS_SUCCESS'
export const HIDE_OLDER_NOTIFICATIONS_SUCCESS = 'HIDE_OLDER_NOTIFICATIONS_SUCCESS'
export const NOTIFICATIONS_PENDING = 'NOTIFICATIONS_PENDING'
Expand Down
5 changes: 4 additions & 1 deletion src/projects/detail/ProjectDetail.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { renderComponent, branch, compose, withProps } from 'recompose'
import { loadProjectDashboard } from '../actions/projectDashboard'
import { clearLoadedProject } from '../actions/project'
import { acceptOrRefuseInvite } from '../actions/projectMember'
import { loadProjects } from '../actions/loadProjects'

import {
LOAD_PROJECT_FAILURE, PROJECT_ROLE_CUSTOMER, PROJECT_ROLE_OWNER,
Expand Down Expand Up @@ -163,6 +164,8 @@ class ProjectDetail extends Component {
status: isJoining ? PROJECT_MEMBER_INVITE_STATUS_ACCEPTED : PROJECT_MEMBER_INVITE_STATUS_REFUSED
}).then(() => {
if(!isJoining) {
// navigate to project listing and reload projects
this.props.loadProjects({ sort: 'updatedAt desc' })
this.props.history.push('/projects/')
} else {
this.props.loadProjectDashboard(this.props.match.params.projectId)
Expand Down Expand Up @@ -229,7 +232,7 @@ const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTime
}
}

const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite }
const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite, loadProjects }

ProjectDetail.propTypes = {
project: PropTypes.object,
Expand Down
12 changes: 11 additions & 1 deletion src/projects/detail/components/Accordion/Accordion.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import cn from 'classnames'
import IconX from '../../../../assets/icons/ui-x-mark.svg'
import IconCarretDown from '../../../../assets/icons/arrow-6px-carret-down-normal.svg'

import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip'
import { TOOLTIP_DEFAULT_DELAY } from '../../../../config/constants'

import './Accordion.scss'

/**
Expand Down Expand Up @@ -132,7 +135,14 @@ class Accordion extends React.Component {
return (
<div styleName={cn('container', { 'is-open': isOpen })}>
<button styleName="header" onClick={this.toggle}>
<h5 styleName="title">{title}</h5>
<Tooltip theme="light" tooltipDelay={TOOLTIP_DEFAULT_DELAY}>
<div className="tooltip-target">
<h5 styleName="title">{title}</h5>
</div>
<div className="tooltip-body">
{title}
</div>
</Tooltip>
<div styleName="value">{this.formatValue()}</div>
<div styleName="toggle">
{isOpen ? <IconX styleName="toggle-icon" /> : <IconCarretDown styleName="toggle-icon" />}
Expand Down
13 changes: 12 additions & 1 deletion src/projects/detail/components/Accordion/Accordion.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
display: none;
}

:global .Tooltip {
width: 180px;

.tooltip-target {
cursor: pointer;
}

.tooltip-body {
white-space: normal;
}
}

& + & {
margin-top: 2 * $base-unit;
}
Expand All @@ -40,7 +52,6 @@

.title {
@include roboto-bold;
flex: 0 0 180px;
font-size: $tc-label-lg;
overflow: hidden;
padding: 0 2 * $base-unit;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,22 @@ import React from 'react'

import './ProjectPlanEmpty.scss'

const ProjectPlanEmpty = () => (
<div styleName="container">
<h2>Welcome to your project plan</h2>
<p>Thank you for submitting your project requirements. In the next 24h someone from our team will reach out to you to discuss the project details with you so we can build the detailed project plan. Until then stand back and relax, we're working hard on your information.</p>
<p>If you feel like you have more things to send over, or want to reach out to us, please drop us a line at connect@topcoder.com. Thanks!</p>
</div>
)
const ProjectPlanEmpty = ({ isManageUser }) => {

return isManageUser ? (
<div styleName="container">
<h2>Build Your Project Plan</h2>
<p>Build your project plan in Connect to reflect delivery progress to the customer. Begin by clicking the "Add Phase" button, select the template that best matches your need, and modify the phase title and milestone dates prior to publishing to the customer.</p>
<p>Important Note: To move the project into 'Active' status, you must set at least one phase in Connect's Project Plan to be in 'Planned' status, which signifies to customers that delivery planning and execution has begun.</p>
<p>If you feel like you have more things to send over, or want to reach out to us, please drop us a line at connect@topcoder.com. Thanks!</p>
</div>
) : (
<div styleName="container">
<h2>Welcome to your project plan</h2>
<p>Thank you for submitting your project requirements. In the next 24h someone from our team will reach out to you to discuss the project details with you so we can build the detailed project plan. Until then stand back and relax, we're working hard on your information.</p>
<p>If you feel like you have more things to send over, or want to reach out to us, please drop us a line at connect@topcoder.com. Thanks!</p>
</div>
)
}

export default ProjectPlanEmpty
1 change: 1 addition & 0 deletions src/projects/detail/components/SpecQuestions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ class SpecQuestions extends React.Component {
case 'textbox':
ChildElem = TCFormFields.Textarea
elemProps.wrapperClass = 'row'
elemProps.rows = 3
elemProps.autoResize = true
if (q.validations) {
elemProps.validations = q.validations
Expand Down
2 changes: 1 addition & 1 deletion src/projects/detail/components/SpecSection.scss
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,7 @@
max-width: 100%;
text-align: left;
text-indent: 0;
resize: none;
resize: vertical;
overflow: hidden;
padding-top: 7px;
padding-bottom: 7px;
Expand Down
2 changes: 1 addition & 1 deletion src/projects/detail/containers/ProjectPlanContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ class ProjectPlanContainer extends React.Component {
}}
/>
) : (
<ProjectPlanEmpty />
<ProjectPlanEmpty isManageUser={isManageUser} />
)}
{isProjectLive && checkPermission(PERMISSIONS.EDIT_PROJECT_PLAN, project, phases) && !isLoadingPhases && (<div styleName="add-button-container">
<Link to={`/projects/${project.id}/add-phase`} className="tc-btn tc-btn-primary tc-btn-sm action-btn">Add New Phase</Link>
Expand Down
Loading