diff --git a/src/api/messages.js b/src/api/messages.js
index 7e0aff8e0..c4149a07d 100644
--- a/src/api/messages.js
+++ b/src/api/messages.js
@@ -45,7 +45,7 @@ export function getTopicPosts(topicId, postIds) {
}
export function createTopic(topicProps) {
- return axios.post(`${TC_API_URL}/v4/topics/`, topicProps)
+ return axios.post(`${TC_API_URL}/v4/topics/`, topicProps, { timeout: 1.5 * 60 * 1000 })
.then( resp => {
return _.get(resp.data, 'result.content', {})
})
@@ -54,7 +54,7 @@ export function createTopic(topicProps) {
// ignore resp
/*eslint-disable no-unused-vars */
export function addTopicPost(topicId, post) {
- return axios.post(`${TC_API_URL}/v4/topics/${topicId}/posts`, { post: post.content } )
+ return axios.post(`${TC_API_URL}/v4/topics/${topicId}/posts`, { post: post.content }, { timeout: 1.5 * 60 * 1000 } )
.then( resp => {
return {
topicId,
diff --git a/src/components/ActionCard/ActionCard.scss b/src/components/ActionCard/ActionCard.scss
index e5cff5a8a..6b8a57410 100644
--- a/src/components/ActionCard/ActionCard.scss
+++ b/src/components/ActionCard/ActionCard.scss
@@ -352,8 +352,18 @@
border-radius: $corner-radius;
}
- a {
+
+ // Link colors
+ a:link,
+ a:visited {
+ color: $tc-dark-blue;
+ text-decoration: none;
+ }
+
+ a:hover,
+ a:active {
color: $tc-dark-blue-70;
+ text-decoration: underline;
}
input {
diff --git a/src/components/Grid/GridView.scss b/src/components/Grid/GridView.scss
index 001d5bf38..64e64a1d1 100644
--- a/src/components/Grid/GridView.scss
+++ b/src/components/Grid/GridView.scss
@@ -410,133 +410,158 @@
/* .pages */
.pages {
- background: $tc-gray-neutral-light;
- min-height: 70px;
- border-bottom-left-radius: 4px;
- border-bottom-right-radius: 4px;
- .right-page {
+ background: $tc-gray-neutral-light;
+ min-height: 70px;
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ .right-page {
+ background: $tc-white;
+ border: 1px solid $tc-gray-30;
+ border-radius: 2px;
+ height: 30px;
+ margin: 20px 20px 0 0;
+ float: right;
+ display: flex;
+ ul {
+ display: flex;
+ }
+ ul,
+ li,
+ .btn-prev,
+ .btn-next {
+ text-align: center;
+ // float: left;
+ }
+ li {
+ border-right: 1px solid $tc-gray-30;
+ position: relative;
+ &.active a {
+ background: $tc-gray-20;
+ box-shadow: inset -1px 1px 2px 0px rgba(71,71,79,0.20);
+ }
+ .down-layer {
background: $tc-white;
- border: 1px solid $tc-gray-30;
- border-radius: 2px;
- height: 30px;
- margin: 20px 20px 0 0;
- float: right;
- ul,
- li,
- .btn-prev,
- .btn-next {
- text-align: center;
- float: left;
+ border-radius: 5px;
+ text-align: left;
+ min-width: 140px;
+ min-height: 55px;
+ margin-bottom: 15px;
+ position: absolute;
+ top: 40px;
+ left: 50%;
+ margin-left: -70px;
+ box-shadow: 0 0 10px rgba(10, 10, 10, 0.2);
+ z-index: 99;
+ &::before {
+ font-size: 0;
+ line-height: 0;
+ width: 0;
+ height: 0;
+ border-left: 7px solid transparent;
+ border-right: 7px solid transparent;
+ border-bottom: 7px solid #fff;
+ content: "";
+ display: block;
+ position: absolute;
+ top: -7px;
+ left: 50%;
+ margin-left: -7px;
}
- li {
- border-right: 1px solid $tc-gray-30;
- position: relative;
- &.active a {
- background: $tc-gray-20;
- box-shadow: inset -1px 1px 2px 0px rgba(71,71,79,0.20);
- }
- .down-layer {
- background: $tc-white;
- border-radius: 5px;
- text-align: left;
- min-width: 140px;
- min-height: 55px;
- margin-bottom: 15px;
- position: absolute;
- top: 40px;
- left: 50%;
- margin-left: -70px;
- box-shadow: 0 0 10px rgba(10, 10, 10, 0.2);
- z-index: 99;
- &::before {
- font-size: 0;
- line-height: 0;
- width: 0;
- height: 0;
- border-left: 7px solid transparent;
- border-right: 7px solid transparent;
- border-bottom: 7px solid #fff;
- content: "";
- display: block;
- position: absolute;
- top: -7px;
- left: 50%;
- margin-left: -7px;
- }
- .txt {
- @include roboto;
- font-size: $tc-label-md;
- color: #394146;
- line-height: $base-unit * 6;
- padding: 13px 0 0 13px;
- display: block;
- float: left;
- }
- .inputs {
- width: 34px;
- height: 30px;
- display: block;
- margin: 13px 0 0 90px;
- input {
- @include roboto;
- font-size: $tc-label-md;
- color: #394146;
- background: $tc-gray-neutral-light;
- line-height: $base-unit * 4;
- text-align: center;
- height: 30px;
- padding: 2px 0 0 0;
- margin: 0;
- }
- }
- }
+ .txt {
+ @include roboto;
+ font-size: $tc-label-md;
+ color: #394146;
+ line-height: $base-unit * 6;
+ padding: 13px 0 0 13px;
+ display: block;
+ float: left;
}
- a {
+ .inputs {
+ width: 34px;
+ height: 30px;
+ display: block;
+ margin: 13px 0 0 90px;
+ input {
@include roboto;
font-size: $tc-label-md;
- color: $tc-gray-80;
- height: 28px;
- min-width: 27px;
- padding: 1px 8px;
- line-height: $base-unit + 23;
- display: block;
- &.btn-prev {
- border-right: 1px solid $tc-gray-30;
- min-width: 70px;
- padding-left: 24px;
- position: relative;
- &:before {
- font-size: 0;
- line-height: 0;
- background: url("./images/arrow-left.svg") no-repeat 0 0;
- width: 12px;
- height: 12px;
- content: "";
- display: block;
- position: absolute;
- top: 9px;
- left: 12px;
- }
- }
- &.btn-next {
- min-width: 70px;
- padding-right: 24px;
- position: relative;
- &:after {
- font-size: 0;
- line-height: 0;
- background: url("./images/arrow-right.svg") no-repeat 0 0;
- width: 12px;
- height: 12px;
- content: "";
- display: block;
- position: absolute;
- top: 9px;
- right: 12px;
- }
- }
+ color: #394146;
+ background: $tc-gray-neutral-light;
+ line-height: $base-unit * 4;
+ text-align: center;
+ height: 30px;
+ padding: 2px 0 0 0;
+ margin: 0;
+ }
}
+ }
+
+ &.go-to-page-pill {
+ .tooltip-target {
+ min-width: 27px;
+ height: 28px;
+ }
+
+ .go-to-page-tooltip {
+ display: flex;
+ align-items: center;
+ @include tc-label-md;
+ line-height: $base-unit * 6;
+
+ input[type="number"] {
+ width: 50px;
+ height: $base-unit * 6;
+ margin-bottom: 0px;
+ margin-left: $base-unit * 2;
+ }
+ }
+ }
+ }
+ a {
+ @include roboto;
+ font-size: $tc-label-md;
+ color: $tc-gray-80;
+ height: 28px;
+ min-width: 27px;
+ padding: 1px 8px;
+ line-height: $base-unit + 23;
+ display: block;
+ &.btn-prev {
+ border-right: 1px solid $tc-gray-30;
+ min-width: 70px;
+ padding-left: 24px;
+ position: relative;
+ &:before {
+ font-size: 0;
+ line-height: 0;
+ background: url("./images/arrow-left.svg") no-repeat 0 0;
+ width: 12px;
+ height: 12px;
+ content: "";
+ display: block;
+ position: absolute;
+ top: 9px;
+ left: 12px;
+ }
+ }
+ &.btn-next {
+ min-width: 70px;
+ padding-right: 24px;
+ position: relative;
+ &:after {
+ font-size: 0;
+ line-height: 0;
+ background: url("./images/arrow-right.svg") no-repeat 0 0;
+ width: 12px;
+ height: 12px;
+ content: "";
+ display: block;
+ position: absolute;
+ top: 9px;
+ right: 12px;
+ }
+ }
}
+ }
}
diff --git a/src/components/Grid/PaginationBar.jsx b/src/components/Grid/PaginationBar.jsx
index 1d59c1f0e..b25face23 100644
--- a/src/components/Grid/PaginationBar.jsx
+++ b/src/components/Grid/PaginationBar.jsx
@@ -2,6 +2,9 @@ import React, { PropTypes } from 'react'
import cn from 'classnames'
import _ from 'lodash'
import { branch, renderComponent } from 'recompose'
+import { Tooltip } from 'appirio-tech-react-components'
+
+const NUMBER_OF_PILLS = 5
const noop = () =>
/**
@@ -26,30 +29,139 @@ const PagePill = ({pageNum, onPageClick, currentPageNum}) => {
)
}
+const GoToPagePill = ({minPage, maxPage, onPageClick}) => {
+ const handleChange = (evt) => {
+ if (evt.keyCode === 13) {// return key
+ onPageClick(parseInt(evt.target.value))
+ }
+ }
+ return (
+
+
+
+
+
+
+ )
+}
+
/**
* Pagingation bar
*/
const PaginationBar = enhance(({onPageChange, currentPageNum, totalCount, pageSize}) => {
const handlePageSelection = val => {
// if it's a number then call handler
- if (currentPageNum !== val && _.isNumber(val)) onPageChange(val)
+ if (currentPageNum !== val && _.isNumber(val)) return onPageChange(val)
// val must be an event- handle prev / next
val = val.target.innerText.toLowerCase()
if (val === 'prev') onPageChange(currentPageNum-1)
if (val === 'next') onPageChange(currentPageNum+1)
}
+ const renderPill = (pageNum) => {
+ return (
+
+ )
+ }
+
+ const populatePills = (pages) => {
+ const pagePills = []
+
+ if (pages <= NUMBER_OF_PILLS) {
+ for(let p=1; p <= pages; p++) {
+ pagePills.push(renderPill(p))
+ }
+ return pagePills
+ }
+ // number of pages available to show pills before current page (assuming 1st pill is always there)
+ const pagesBeforeCurrent = currentPageNum - 1 - 1
+ // number of pages available to show pills after current page (assuming last pill is always there)
+ const pagesAfterCurrent = pages - currentPageNum - 1
+ // render first page pill
+ if (currentPageNum > 1) {
+ pagePills.push(renderPill(1))
+ }
+ // render current page pill
+ pagePills.push(renderPill(currentPageNum))
+
+ // render last page pill
+ if (currentPageNum < pages) {
+ pagePills.push(renderPill(pages))
+ }
+ // array to store the pills to be rendered on the left side of the current page
+ const leftPagePills = []
+ // initially assumes equal partition between left and right side of the current page
+ let remainingPillsLeft = Math.ceil((NUMBER_OF_PILLS - pagePills.length) / 2)
+
+ // if pills avialable to render before current page, add pills determined by remainingPillsLeft
+ if (pagesBeforeCurrent > 0) {
+ for(let i=1; i <= remainingPillsLeft && i <= pagesBeforeCurrent; i++) {
+ leftPagePills.splice(0, 0, renderPill(currentPageNum - i))
+ }
+ }
+ // add the left side pills to the main pills array
+ pagePills.splice(1, 0, ...leftPagePills)
+ // calculates the pills to render on the right side of the current page, it may be greater than the left side pills
+ const remainingPillsRight = NUMBER_OF_PILLS - pagePills.length
+
+ // if pills available to render after the current page, add pills determined by remainingPillsRight
+ if (pagesAfterCurrent > 0) {
+ for(let i=1; i <= remainingPillsRight && i <= pagesAfterCurrent; i++) {
+ pagePills.splice(pagePills.length - 1, 0, renderPill(currentPageNum + i))
+ }
+ }
+ // Now, checks if we still need to render any pill to show fix number of pills
+ remainingPillsLeft = NUMBER_OF_PILLS - pagePills.length
+ // adds more pills towards left of the current page
+ if(pagesBeforeCurrent - leftPagePills.length - remainingPillsLeft > 0) {
+ for(let i=1; i <= remainingPillsLeft; i++) {
+ pagePills.splice(1, 0, renderPill(currentPageNum - leftPagePills.length - i))
+ }
+ }
+ // adds go to page pill if there are missing pills between first and current page pill
+ if (pagesBeforeCurrent - leftPagePills.length - remainingPillsLeft > 0) {
+ pagePills.splice(1,
+ 0,
+
+ )
+ }
+ // adds go to page pill if there are missing pills between current page and last pill
+ if (pagesAfterCurrent - remainingPillsRight > 0) {
+ pagePills.splice(pagePills.length - 1,
+ 0,
+
+ )
+ }
+ return pagePills
+ }
+
// calculate number of pages to show
const pages = Math.ceil(totalCount / pageSize)
+ const pagePills = populatePills(pages)
return (
{ currentPageNum > 1 && Prev }
- { _.range(pages).map(idx =>
-
- )}
+ { pagePills.map(pagePill => pagePill)}
{ currentPageNum < pages && Next }
diff --git a/src/projects/detail/containers/FeedContainer.js b/src/projects/detail/containers/FeedContainer.js
index f621ca219..f2b60bd31 100644
--- a/src/projects/detail/containers/FeedContainer.js
+++ b/src/projects/detail/containers/FeedContainer.js
@@ -54,7 +54,7 @@ class FeedView extends React.Component {
}
componentWillReceiveProps(nextProps) {
- this.init(nextProps)
+ this.init(nextProps, this.props)
}
componentWillUnmount() {
@@ -128,11 +128,23 @@ class FeedView extends React.Component {
return item
}
- init(props) {
+ init(props, prevProps) {
const { feeds } = props
+ let resetNewPost = false
+ if (prevProps) {
+ resetNewPost = prevProps.isCreatingFeed && !props.isCreatingFeed && !props.error
+ }
this.setState({
+ newPost: resetNewPost ? {} : this.state.newPost,
feeds: feeds.map((feed) => {
- return this.mapFeed(feed, this.state.showAll.indexOf(feed.id) > -1)
+ // finds the same feed from previous props, if exists
+ let prevFeed
+ if (prevProps && prevProps.feeds) {
+ prevFeed = _.find(prevProps.feeds, f => feed.id === f.id)
+ }
+ // reset new comment if we were adding comment and there is no error in doing so
+ const resetNewComment = prevFeed && prevFeed.isAddingComment && !feed.isAddingComment && !feed.error
+ return this.mapFeed(feed, this.state.showAll.indexOf(feed.id) > -1, resetNewComment)
}).filter(item => item)
})
}
diff --git a/src/projects/detail/containers/MessagesContainer.js b/src/projects/detail/containers/MessagesContainer.js
index 6b3455a84..44fdc238a 100644
--- a/src/projects/detail/containers/MessagesContainer.js
+++ b/src/projects/detail/containers/MessagesContainer.js
@@ -63,7 +63,7 @@ class MessagesView extends React.Component {
}
componentWillReceiveProps(nextProps) {
- this.init(nextProps)
+ this.init(nextProps, this.props)
}
componentWillUnmount() {
@@ -138,20 +138,32 @@ class MessagesView extends React.Component {
return item
}
- init(props) {
+ init(props, prevProps) {
const { activeThreadId } = this.state
const propsThreadId = _.get(props, 'location.state.threadId', null)
const threadId = activeThreadId ? activeThreadId : propsThreadId
const activeThreadIndex = threadId
? _.findIndex(props.threads, (thread) => thread.id === threadId )
: 0
-
+ let resetNewPost = false
+ if (prevProps) {
+ resetNewPost = prevProps.isCreatingFeed && !props.isCreatingFeed && !props.error
+ }
this.setState({
+ newPost: resetNewPost ? {} : this.state.newPost,
scrollPosition: activeThreadIndex * 71,
threads: props.threads.map((thread, idx) => {
+ // finds the same thread from previous props, if exists
+ let prevThread
+ if (prevProps && prevProps.threads) {
+ prevThread = _.find(prevProps.threads, t => thread.id === t.id)
+ }
+ // reset new message if we were adding message and there is no error in doing so
+ const resetNewMessage = prevThread && prevThread.isAddingComment && !thread.isAddingComment && !thread.error
return this.mapFeed(thread,
idx === activeThreadIndex,
- this.state.showAll.indexOf(thread.id) > -1)
+ this.state.showAll.indexOf(thread.id) > -1,
+ resetNewMessage)
}).filter(item => item)
})
}
diff --git a/src/projects/list/components/Projects/Projects.jsx b/src/projects/list/components/Projects/Projects.jsx
index a3555cd21..5c473e73d 100755
--- a/src/projects/list/components/Projects/Projects.jsx
+++ b/src/projects/list/components/Projects/Projects.jsx
@@ -12,6 +12,7 @@ class Projects extends Component {
this.sortHandler = this.sortHandler.bind(this)
this.onPageChange = this.onPageChange.bind(this)
this.applyFilters = this.applyFilters.bind(this)
+ this.init = this.init.bind(this)
}
componentDidUpdate() {
@@ -23,13 +24,26 @@ class Projects extends Component {
window.sessionStorage.setItem('projectsPageScrollTop', scrollingElement.scrollTop)
}
+ componentWillReceiveProps(nextProps) {
+ console.log(nextProps)
+ const prevQueryParams = _.get(this.props, 'location.query', null)
+ const queryParams = _.get(nextProps, 'location.query', null)
+ if (!_.isEqual(prevQueryParams, queryParams)) {
+ this.init(nextProps)
+ }
+ }
+
componentWillMount() {
+ this.init(this.props)
+ }
+
+ init(props) {
document.title = 'Projects - Topcoder'
// this.searchTermFromQuery = this.props.location.query.q || ''
- const {criteria, loadProjects} = this.props
- let pageNum = this.props.pageNum
+ const {criteria, loadProjects} = props
+ let pageNum = props.pageNum
// check for criteria specified in URL.
- const queryParams = _.get(this.props, 'location.query', null)
+ const queryParams = _.get(props, 'location.query', null)
if (!_.isEmpty(queryParams)) {
const initialCriteria = {}
if (queryParams.sort) initialCriteria.sort = queryParams.sort
diff --git a/src/projects/reducers/projectTopics.js b/src/projects/reducers/projectTopics.js
index c3fa25651..01c2c16dd 100644
--- a/src/projects/reducers/projectTopics.js
+++ b/src/projects/reducers/projectTopics.js
@@ -243,7 +243,7 @@ export const projectTopics = function (state=initialState, action) {
const feed = state.feeds[tag].topics[feedIndex]
const updatedFeed = update (feed, {
isAddingComment : { $set : false },
- error: { $set: false }
+ error: { $set: true }
})
const feedUpdateQuery = {}
feedUpdateQuery[tag] = { topics: { $splice: [[feedIndex, 1, updatedFeed]] } }