Skip to content

Commit 856d716

Browse files
author
vikasrohit
authored
Merge pull request #3168 from appirio-tech/cf18
Cf18
2 parents 77bfaf6 + fd57215 commit 856d716

File tree

18 files changed

+168
-70
lines changed

18 files changed

+168
-70
lines changed

src/api/projects.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { TC_API_URL, PROJECTS_API_URL, PROJECTS_LIST_PER_PAGE } from '../config/
44

55
export function getProjects(criteria, pageNum) {
66
// add default params
7-
const includeFields = ['id', 'name', 'description', 'members', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version']
7+
const includeFields = ['id', 'name', 'description', 'members', 'status', 'type', 'actualPrice', 'estimatedPrice', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy', 'details', 'lastActivityAt', 'lastActivityUserId', 'version', 'templateId']
88
const params = {
99
limit: PROJECTS_LIST_PER_PAGE,
1010
offset: (pageNum - 1) * PROJECTS_LIST_PER_PAGE,

src/components/NotificationsDropdown/NotificationsDropdown.jsx

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,48 @@ import EnhancedDropdown from './EnhancedDropdown'
99
import NotificationsBell from './NotificationsBell'
1010

1111

12-
const NotificationsDropdown = (props) => {
13-
return (
14-
<div className="notifications-dropdown">
15-
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={props.onToggle}>
16-
<div className="dropdown-menu-header">
17-
<NotificationsBell
18-
hasUnread={props.hasUnread}
19-
hasNew={props.hasNew}
20-
onClick={props.onToggle}
21-
/>
22-
</div>
23-
<div className="dropdown-menu-list">
24-
<div className="notifications-dropdown-content">
25-
{props.children}
12+
class NotificationsDropdown extends React.Component {
13+
14+
constructor(props) {
15+
super(props)
16+
this.state = {
17+
isOpen: false
18+
}
19+
20+
this.toggle = this.toggle.bind(this)
21+
}
22+
23+
toggle(isOpen) {
24+
if (typeof isOpen === 'object') {
25+
this.props.onToggle(!this.state.isOpen)
26+
this.setState({ isOpen: !this.state.isOpen})
27+
} else {
28+
this.props.onToggle(isOpen)
29+
this.setState({ isOpen })
30+
}
31+
}
32+
33+
render() {
34+
const { hasUnread, hasNew, children } = this.props
35+
return (
36+
<div className="notifications-dropdown">
37+
<EnhancedDropdown theme="UserDropdownMenu" pointerShadow noAutoclose onToggle={this.toggle}>
38+
<div className="dropdown-menu-header">
39+
<NotificationsBell
40+
hasUnread={hasUnread}
41+
hasNew={hasNew}
42+
onClick={this.toggle}
43+
/>
44+
</div>
45+
<div className="dropdown-menu-list">
46+
<div className="notifications-dropdown-content">
47+
{children}
48+
</div>
2649
</div>
27-
</div>
28-
</EnhancedDropdown>
29-
</div>
30-
)
50+
</EnhancedDropdown>
51+
</div>
52+
)
53+
}
3154
}
3255

3356
NotificationsDropdown.propTypes = {

src/components/NotificationsDropdown/NotificationsDropdownContainer.jsx

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ import { Link } from 'react-router-dom'
88
import { connect } from 'react-redux'
99
import _ from 'lodash'
1010
import { TransitionGroup, Transition } from 'react-transition-group'
11-
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, toggleNotificationRead, visitNotifications,
11+
import { getNotifications, toggleNotificationSeen, markAllNotificationsRead, markAllNotificationsSeen, toggleNotificationRead,
1212
toggleBundledNotificationRead, viewOlderNotifications, hideOlderNotifications } from '../../routes/notifications/actions'
1313
import {
1414
splitNotificationsBySources,
1515
filterReadNotifications,
16+
filterSeenNotifications,
1617
limitQuantityInSources,
1718
preRenderNotifications,
1819
} from '../../routes/notifications/helpers/notifications'
@@ -29,9 +30,9 @@ import { NOTIFICATIONS_DROPDOWN_PER_SOURCE, NOTIFICATIONS_NEW_PER_SOURCE, REFRES
2930
import './NotificationsDropdown.scss'
3031

3132
const NotificationsDropdownContainerView = (props) => {
32-
const {initialized, isLoading, lastVisited, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
33-
pending, toggleBundledNotificationRead, visitNotifications, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
34-
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb } = props
33+
const {initialized, isLoading, sources, notifications, markAllNotificationsRead, toggleNotificationRead, toggleNotificationSeen,
34+
pending, toggleBundledNotificationRead, oldSourceIds, viewOlderNotifications, isDropdownMobileOpen, isDropdownWebOpen,
35+
toggleNotificationsDropdownMobile, toggleNotificationsDropdownWeb, markAllNotificationsSeen } = props
3536
if (!initialized && isLoading) {
3637
return (
3738
<NotificationsDropdown hasUnread={false}>
@@ -51,9 +52,11 @@ const NotificationsDropdownContainerView = (props) => {
5152
}
5253

5354
const notReadNotifications = filterReadNotifications(notifications)
55+
const notSeenNotifications = filterSeenNotifications(notifications)
5456
const allNotificationsBySources = splitNotificationsBySources(sources, notReadNotifications)
5557

5658
const hasUnread = notReadNotifications.length > 0
59+
const hasUnseen = notSeenNotifications.length > 0
5760
// we have to give Dropdown component some time
5861
// before removing notification item node from the list
5962
// otherwise dropdown thinks we clicked outside and closes dropdown
@@ -70,7 +73,7 @@ const NotificationsDropdownContainerView = (props) => {
7073
}, 0)
7174
}
7275
}
73-
const hasNew = hasUnread && lastVisited < _.maxBy(_.map(notifications, n => new Date(n.date)))
76+
7477
let notificationsEmpty = (
7578
<NotificationsEmpty>
7679
<p className="notifications-empty-note">
@@ -92,6 +95,12 @@ const NotificationsDropdownContainerView = (props) => {
9295
)
9396
}
9497

98+
const markNotificationsSeen = (isOpen) => {
99+
if (isOpen) {
100+
markAllNotificationsSeen(null, notifications)
101+
}
102+
}
103+
95104
// this function checks that notification is not seen yet,
96105
// before marking it as seen
97106
const markNotificationSeen = (notificationId) => {
@@ -118,10 +127,10 @@ const NotificationsDropdownContainerView = (props) => {
118127
return (
119128
<NotificationsDropdown
120129
hasUnread={hasUnread}
121-
hasNew={hasNew}
130+
hasNew={hasUnseen}
122131
onToggle={(isOpen) => {
123132
toggleNotificationsDropdownWeb(isOpen)
124-
visitNotifications()
133+
markNotificationsSeen(isOpen)
125134
}}
126135
>
127136
{isDropdownWebOpen && <div>
@@ -192,10 +201,9 @@ const NotificationsDropdownContainerView = (props) => {
192201
return (
193202
<NotificationsMobilePage
194203
hasUnread={hasUnread}
195-
hasNew={hasNew}
204+
hasNew={hasUnseen}
196205
onToggle={() => {
197206
toggleNotificationsDropdownMobile()
198-
visitNotifications()
199207
}}
200208
isOpen={isDropdownMobileOpen}
201209
>
@@ -253,21 +261,18 @@ class NotificationsDropdownContainer extends React.Component {
253261
constructor(props) {
254262
super(props)
255263
this.state = {
256-
lastVisited: new Date(0),
257264
isDropdownWebOpen: false,
258265
isDropdownMobileOpen: false,
259266
notificationsVisited: false,
260267
}
261268

262269
this.onToggleNotificationsDropdownWeb = this.onToggleNotificationsDropdownWeb.bind(this)
263270
this.onToggleNotificationsDropdownMobile = this.onToggleNotificationsDropdownMobile.bind(this)
264-
this.onVisitNotifications = this.onVisitNotifications.bind(this)
265271
}
266272

267273
componentDidMount() {
268274
this.props.getNotifications()
269275
this.autoRefreshNotifications = setInterval(() => this.props.getNotifications(), REFRESH_NOTIFICATIONS_INTERVAL)
270-
this.setState({ lastVisited: this.props.lastVisited })
271276
}
272277

273278
componentWillUnmount() {
@@ -276,7 +281,6 @@ class NotificationsDropdownContainer extends React.Component {
276281
this.onToggleNotificationsDropdownMobile(false)
277282
this.onToggleNotificationsDropdownWeb(false)
278283
this.props.hideOlderNotifications()
279-
this.state.notificationsVisited && this.props.visitNotifications()
280284
}
281285

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

303-
onVisitNotifications() {
304-
this.setState({
305-
lastVisited: _.maxBy(_.map(this.props.notifications, n => new Date(n.date))),
306-
notificationsVisited: true
307-
})
308-
}
309-
310307
render() {
311308
const { notifications, ...restProps } = this.props
312309
const preRenderedNotifications = preRenderNotifications(notifications)
313-
314310
return (
315311
<NotificationsDropdownContainerView
316312
{...restProps}
317313
notifications={preRenderedNotifications}
318314
toggleNotificationsDropdownWeb={this.onToggleNotificationsDropdownWeb}
319315
toggleNotificationsDropdownMobile={this.onToggleNotificationsDropdownMobile}
320-
visitNotifications={this.onVisitNotifications}
321-
lastVisited={this.state.lastVisited}
322316
isDropdownMobileOpen={this.state.isDropdownMobileOpen}
323317
isDropdownWebOpen={this.state.isDropdownWebOpen}
324318
/>
@@ -331,9 +325,9 @@ const mapStateToProps = ({ notifications }) => notifications
331325

332326
const mapDispatchToProps = {
333327
getNotifications,
334-
visitNotifications,
335328
toggleNotificationSeen,
336329
markAllNotificationsRead,
330+
markAllNotificationsSeen,
337331
toggleNotificationRead,
338332
toggleBundledNotificationRead,
339333
viewOlderNotifications,

src/config/constants.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ export const LOAD_ORG_CONFIG_FAILURE = 'LOAD_ORG_CONFIG_FAILURE'
1515
export const GET_NOTIFICATIONS_PENDING = 'GET_NOTIFICATIONS_PENDING'
1616
export const GET_NOTIFICATIONS_SUCCESS = 'GET_NOTIFICATIONS_SUCCESS'
1717
export const GET_NOTIFICATIONS_FAILURE = 'GET_NOTIFICATIONS_FAILURE'
18-
export const VISIT_NOTIFICATIONS = 'VISIT_NOTIFICATIONS'
1918
export const SET_NOTIFICATIONS_FILTER_BY = 'SET_NOTIFICATIONS_FILTER_BY'
2019
export const MARK_ALL_NOTIFICATIONS_READ = 'MARK_ALL_NOTIFICATIONS_READ'
2120
export const TOGGLE_NOTIFICATION_READ = 'TOGGLE_NOTIFICATION_READ'
2221
export const TOGGLE_NOTIFICATION_SEEN = 'TOGGLE_NOTIFICATION_SEEN'
22+
export const MARK_ALL_NOTIFICATIONS_SEEN = 'MARK_ALL_NOTIFICATIONS_SEEN'
2323
export const VIEW_OLDER_NOTIFICATIONS_SUCCESS = 'VIEW_OLDER_NOTIFICATIONS_SUCCESS'
2424
export const HIDE_OLDER_NOTIFICATIONS_SUCCESS = 'HIDE_OLDER_NOTIFICATIONS_SUCCESS'
2525
export const NOTIFICATIONS_PENDING = 'NOTIFICATIONS_PENDING'

src/projects/detail/ProjectDetail.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { renderComponent, branch, compose, withProps } from 'recompose'
88
import { loadProjectDashboard } from '../actions/projectDashboard'
99
import { clearLoadedProject } from '../actions/project'
1010
import { acceptOrRefuseInvite } from '../actions/projectMember'
11+
import { loadProjects } from '../actions/loadProjects'
1112

1213
import {
1314
LOAD_PROJECT_FAILURE, PROJECT_ROLE_CUSTOMER, PROJECT_ROLE_OWNER,
@@ -163,6 +164,8 @@ class ProjectDetail extends Component {
163164
status: isJoining ? PROJECT_MEMBER_INVITE_STATUS_ACCEPTED : PROJECT_MEMBER_INVITE_STATUS_REFUSED
164165
}).then(() => {
165166
if(!isJoining) {
167+
// navigate to project listing and reload projects
168+
this.props.loadProjects({ sort: 'updatedAt desc' })
166169
this.props.history.push('/projects/')
167170
} else {
168171
this.props.loadProjectDashboard(this.props.match.params.projectId)
@@ -229,7 +232,7 @@ const mapStateToProps = ({projectState, projectDashboard, loadUser, productsTime
229232
}
230233
}
231234

232-
const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite }
235+
const mapDispatchToProps = { loadProjectDashboard, clearLoadedProject, acceptOrRefuseInvite, loadProjects }
233236

234237
ProjectDetail.propTypes = {
235238
project: PropTypes.object,

src/projects/detail/components/Accordion/Accordion.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import cn from 'classnames'
1111
import IconX from '../../../../assets/icons/ui-x-mark.svg'
1212
import IconCarretDown from '../../../../assets/icons/arrow-6px-carret-down-normal.svg'
1313

14+
import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip'
15+
import { TOOLTIP_DEFAULT_DELAY } from '../../../../config/constants'
16+
1417
import './Accordion.scss'
1518

1619
/**
@@ -132,7 +135,14 @@ class Accordion extends React.Component {
132135
return (
133136
<div styleName={cn('container', { 'is-open': isOpen })}>
134137
<button styleName="header" onClick={this.toggle}>
135-
<h5 styleName="title">{title}</h5>
138+
<Tooltip theme="light" tooltipDelay={TOOLTIP_DEFAULT_DELAY}>
139+
<div className="tooltip-target">
140+
<h5 styleName="title">{title}</h5>
141+
</div>
142+
<div className="tooltip-body">
143+
{title}
144+
</div>
145+
</Tooltip>
136146
<div styleName="value">{this.formatValue()}</div>
137147
<div styleName="toggle">
138148
{isOpen ? <IconX styleName="toggle-icon" /> : <IconCarretDown styleName="toggle-icon" />}

src/projects/detail/components/Accordion/Accordion.scss

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@
1919
display: none;
2020
}
2121

22+
:global .Tooltip {
23+
width: 180px;
24+
25+
.tooltip-target {
26+
cursor: pointer;
27+
}
28+
29+
.tooltip-body {
30+
white-space: normal;
31+
}
32+
}
33+
2234
& + & {
2335
margin-top: 2 * $base-unit;
2436
}
@@ -40,7 +52,6 @@
4052

4153
.title {
4254
@include roboto-bold;
43-
flex: 0 0 180px;
4455
font-size: $tc-label-lg;
4556
overflow: hidden;
4657
padding: 0 2 * $base-unit;

src/projects/detail/components/ProjectPlanEmpty/ProjectPlanEmpty.jsx

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@ import React from 'react'
55

66
import './ProjectPlanEmpty.scss'
77

8-
const ProjectPlanEmpty = () => (
9-
<div styleName="container">
10-
<h2>Welcome to your project plan</h2>
11-
<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>
12-
<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>
13-
</div>
14-
)
8+
const ProjectPlanEmpty = ({ isManageUser }) => {
9+
10+
return isManageUser ? (
11+
<div styleName="container">
12+
<h2>Build Your Project Plan</h2>
13+
<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>
14+
<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>
15+
<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>
16+
</div>
17+
) : (
18+
<div styleName="container">
19+
<h2>Welcome to your project plan</h2>
20+
<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>
21+
<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>
22+
</div>
23+
)
24+
}
1525

1626
export default ProjectPlanEmpty

src/projects/detail/components/SpecQuestions.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ class SpecQuestions extends React.Component {
211211
case 'textbox':
212212
ChildElem = TCFormFields.Textarea
213213
elemProps.wrapperClass = 'row'
214+
elemProps.rows = 3
214215
elemProps.autoResize = true
215216
if (q.validations) {
216217
elemProps.validations = q.validations

src/projects/detail/components/SpecSection.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,7 @@
729729
max-width: 100%;
730730
text-align: left;
731731
text-indent: 0;
732-
resize: none;
732+
resize: vertical;
733733
overflow: hidden;
734734
padding-top: 7px;
735735
padding-bottom: 7px;

0 commit comments

Comments
 (0)