diff --git a/src/components/Grid/GridView.jsx b/src/components/Grid/GridView.jsx index e9200055c..c589e7ae9 100644 --- a/src/components/Grid/GridView.jsx +++ b/src/components/Grid/GridView.jsx @@ -18,7 +18,7 @@ const GridView = props => { const headerProps = { columns, sortHandler, currentSortField } const renderItem = (item, index) => { - return item.isPlaceholder ? : + return item.isPlaceholder ? : } const handleLoadMore = () => { diff --git a/src/projects/actions/phasesTopics.js b/src/projects/actions/phasesTopics.js index 1175cb4e6..9c464ce75 100644 --- a/src/projects/actions/phasesTopics.js +++ b/src/projects/actions/phasesTopics.js @@ -37,8 +37,6 @@ const getPhaseTopicWithMember = (dispatch, projectId, phaseId, tag) => { return new Promise((resolve, reject) => { return dispatch({ type: LOAD_PHASE_FEED, - // TODO $PROJECT_PLAN$ remove getting topics for project 5021 - // and uncomment calling for getting topics for phase payload: getTopicsWithComments('project', `${projectId}`, `phase#${phaseId}`, false), meta: { tag, phaseId } }) diff --git a/src/projects/actions/project.js b/src/projects/actions/project.js index 923879cf0..2f4fd5f38 100644 --- a/src/projects/actions/project.js +++ b/src/projects/actions/project.js @@ -44,7 +44,11 @@ import { MILESTONE_STATUS, PHASE_STATUS_ACTIVE, PHASE_DIRTY, - PHASE_DIRTY_UNDO + PHASE_DIRTY_UNDO, + PROJECT_STATUS_IN_REVIEW, + PHASE_STATUS_REVIEWED, + PROJECT_STATUS_REVIEWED, + PROJECT_STATUS_ACTIVE } from '../../config/constants' import { updateProductMilestone, @@ -434,7 +438,36 @@ export function updatePhase(projectId, phaseId, updatedProps, phaseIndex) { } else { optionallyUpdateFirstMilestone() } - return true + + // update project caused by phase updates + }).then(() => { + const project = state.projectState.project + + // if one phase moved to REVIEWED status, make project IN_REVIEW too + if ( + _.includes([PROJECT_STATUS_DRAFT], project.status) && + phase.status !== PHASE_STATUS_REVIEWED && + updatedProps.status === PHASE_STATUS_REVIEWED + ) { + dispatch( + updateProject(projectId, { + status: PROJECT_STATUS_IN_REVIEW + }, true) + ) + } + + // if one phase moved to ACTIVE status, make project ACTIVE too + if ( + _.includes([PROJECT_STATUS_DRAFT, PROJECT_STATUS_IN_REVIEW, PROJECT_STATUS_REVIEWED], project.status) && + phase.status !== PHASE_STATUS_ACTIVE && + updatedProps.status === PHASE_STATUS_ACTIVE + ) { + dispatch( + updateProject(projectId, { + status: PROJECT_STATUS_ACTIVE + }, true) + ) + } }) } } diff --git a/src/projects/detail/components/SpecSection.jsx b/src/projects/detail/components/SpecSection.jsx index dc775c912..5bff01960 100644 --- a/src/projects/detail/components/SpecSection.jsx +++ b/src/projects/detail/components/SpecSection.jsx @@ -196,7 +196,7 @@ const SpecSection = props => { } case 'project-name': { const refCodeFieldName = 'details.utm.code' - const refCode = _.get(project, refCodeFieldName, undefined) + const refCode = _.get(project, refCodeFieldName, '') const queryParamRefCode = qs.parse(window.location.search).refCode return (
@@ -204,7 +204,7 @@ const SpecSection = props => { @@ -273,6 +273,7 @@ class Milestone extends React.Component { @@ -350,6 +351,7 @@ class Milestone extends React.Component { { + class MilestoneExtensionRequest extends React.Component { + constructor(props) { + super(props) + + this.state = { + isShowExtensionRequestMessage: false, + } + + this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) + this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) + this.requestExtension = this.requestExtension.bind(this) + this.approveExtension = this.approveExtension.bind(this) + this.declineExtension = this.declineExtension.bind(this) + } + + showExtensionRequestMessage() { + this.setState({ + isShowExtensionRequestMessage: true, + isSelectWarningVisible: false, + }) + } + + hideExtensionRequestMessage() { + this.setState({ isShowExtensionRequestMessage: false }) + } + + requestExtension(value) { + const { updateMilestoneContent } = this.props + + const extensionDuration = parseInt(value, 10) + + updateMilestoneContent({ + extensionRequest: { + duration: extensionDuration, + } + }) + } + + declineExtension() { + const { updateMilestoneContent } = this.props + + updateMilestoneContent({ + extensionRequest: null, + }) + } + + approveExtension() { + const { extendMilestone, milestone } = this.props + const content = _.get(milestone, 'details.content') + const extensionRequest = _.get(milestone, 'details.content.extensionRequest') + + extendMilestone(extensionRequest.duration, { + details: { + ...milestone.details, + content: { + ...content, + extensionRequest: null, + } + } + }) + } + + render() { + const { milestone } = this.props + const { isShowExtensionRequestMessage } = this.state + + const extensionRequest = _.get(milestone, 'details.content.extensionRequest') + + const extensionRequestDialog = isShowExtensionRequestMessage ? ( + + ) : null + + const extensionRequestButton = !extensionRequest ? ( + + ) : null + + const extensionRequestConfirmation = extensionRequest ? ( +
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} + buttons={[ + { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, + { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, + ]} + /> + ) : null + + return ( + + ) + } + } + + MilestoneExtensionRequest.propTypes = { + extendMilestone: PT.func.isRequired, + milestone: PT.object.isRequired, + updateMilestoneContent: PT.func.isRequired, + } + + return MilestoneExtensionRequest +} \ No newline at end of file diff --git a/src/projects/detail/components/timeline/MilestoneExtensionRequest/index.js b/src/projects/detail/components/timeline/MilestoneExtensionRequest/index.js new file mode 100644 index 000000000..2741fee15 --- /dev/null +++ b/src/projects/detail/components/timeline/MilestoneExtensionRequest/index.js @@ -0,0 +1,2 @@ +import { withMilestoneExtensionRequest } from './MilestoneExtensionRequest' +export { withMilestoneExtensionRequest } diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeAddLinks/MilestoneTypeAddLinks.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeAddLinks/MilestoneTypeAddLinks.jsx index 0e61a9ecc..30c9f02bf 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeAddLinks/MilestoneTypeAddLinks.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeAddLinks/MilestoneTypeAddLinks.jsx @@ -12,6 +12,7 @@ import LinkList from '../../LinkList' import MilestonePostMessage from '../../MilestonePostMessage' import ProjectProgress from '../../../ProjectProgress' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS @@ -25,8 +26,6 @@ class MilestoneTypeAddLinks extends React.Component { this.state = { addedLinks: [], - isShowExtensionRequestMessage: false, - isShowExtensionConfirmMessage: false, isShowCompleteConfirmMessage: false, isLinkAdded: true, } @@ -37,11 +36,6 @@ class MilestoneTypeAddLinks extends React.Component { this.hideCompleteAddLinksConfirmation = this.hideCompleteAddLinksConfirmation.bind(this) this.complete = this.complete.bind(this) this.toggleRejectedSection = this.toggleRejectedSection.bind(this) - this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) - this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) - this.requestExtension = this.requestExtension.bind(this) - this.approveExtension = this.approveExtension.bind(this) - this.declineExtension = this.declineExtension.bind(this) } showCompleteAddLinksConfirmation() { @@ -78,52 +72,6 @@ class MilestoneTypeAddLinks extends React.Component { }) } - showExtensionRequestMessage() { - this.setState({ - isShowExtensionRequestMessage: true - }) - } - - hideExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: false }) - } - - requestExtension(value) { - const { updateMilestoneContent } = this.props - - const extensionDuration = parseInt(value, 10) - - updateMilestoneContent({ - extensionRequest: { - duration: extensionDuration, - } - }) - } - - declineExtension() { - const { updateMilestoneContent } = this.props - - updateMilestoneContent({ - extensionRequest: null, - }) - } - - approveExtension() { - const { extendMilestone, milestone } = this.props - const content = _.get(milestone, 'details.content') - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - - extendMilestone(extensionRequest.duration, { - details: { - ...milestone.details, - content: { - ...content, - extensionRequest: null, - } - } - }) - } - addUrl(values) { const { addedLinks } = this.state values.type = 'marvelapp' @@ -157,17 +105,16 @@ class MilestoneTypeAddLinks extends React.Component { milestone, theme, currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, } = this.props const { addedLinks, - isShowExtensionRequestMessage, isShowCompleteConfirmMessage, - isShowExtensionConfirmMessage, isLinkAdded, } = this.state - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - const isActive = milestone.status === MILESTONE_STATUS.ACTIVE const isCompleted = milestone.status === MILESTONE_STATUS.COMPLETED const today = moment().hours(0).minutes(0).seconds(0).milliseconds(0) @@ -197,16 +144,16 @@ class MilestoneTypeAddLinks extends React.Component { {isActive && (
-
- + +
- -
+
+ {!currentUser.isCustomer && ( @@ -235,35 +182,18 @@ class MilestoneTypeAddLinks extends React.Component { )}
- {isShowExtensionRequestMessage && ( + {!!extensionRequestDialog && (
- + {extensionRequestDialog}
)} - {!!extensionRequest && ( - + {!!extensionRequestConfirmation && ( +
-
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} - buttons={[ - { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, - { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, - ]} - /> + {extensionRequestConfirmation}
)} @@ -287,10 +217,9 @@ class MilestoneTypeAddLinks extends React.Component { { !isCompleted && - !isShowExtensionRequestMessage && - !isShowExtensionConfirmMessage && + !extensionRequestDialog && !isShowCompleteConfirmMessage && - (!currentUser.isCustomer) && + !currentUser.isCustomer && (
@@ -299,17 +228,10 @@ class MilestoneTypeAddLinks extends React.Component { className={'tc-btn tc-btn-primary'} onClick={!currentUser.isCustomer ? this.showCompleteAddLinksConfirmation : this.complete} > - Complete - - )} - {!currentUser.isCustomer && !extensionRequest && ( - )} + {!currentUser.isCustomer && extensionRequestButton}
)} @@ -328,10 +250,12 @@ MilestoneTypeAddLinks.defaultProps = { MilestoneTypeAddLinks.propTypes = { completeMilestone: PT.func.isRequired, currentUser: PT.object.isRequired, - extendMilestone: PT.func.isRequired, milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeAddLinks +export default withMilestoneExtensionRequest(MilestoneTypeAddLinks) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReview/MilestoneTypeCheckpointReview.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReview/MilestoneTypeCheckpointReview.jsx index fec777c9a..836688af1 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReview/MilestoneTypeCheckpointReview.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReview/MilestoneTypeCheckpointReview.jsx @@ -12,6 +12,7 @@ import LinkList from '../../LinkList' import MilestonePostMessage from '../../MilestonePostMessage' import ProjectProgress from '../../../ProjectProgress' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS, @@ -28,8 +29,6 @@ class MilestoneTypeCheckpointReview extends React.Component { selectedLinks: [], isInReview: false, isSelectWarningVisible: false, - isShowExtensionRequestMessage: false, - isShowExtensionConfirmMessage: false, isShowCompleteConfirmMessage: false, } @@ -40,11 +39,6 @@ class MilestoneTypeCheckpointReview extends React.Component { this.hideCompleteReviewConfirmation = this.hideCompleteReviewConfirmation.bind(this) this.completeReview = this.completeReview.bind(this) this.toggleRejectedSection = this.toggleRejectedSection.bind(this) - this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) - this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) - this.requestExtension = this.requestExtension.bind(this) - this.approveExtension = this.approveExtension.bind(this) - this.declineExtension = this.declineExtension.bind(this) this.moveToReviewingState = this.moveToReviewingState.bind(this) } @@ -105,53 +99,6 @@ class MilestoneTypeCheckpointReview extends React.Component { }) } - showExtensionRequestMessage() { - this.setState({ - isShowExtensionRequestMessage: true, - isSelectWarningVisible: false, - }) - } - - hideExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: false }) - } - - requestExtension(value) { - const { updateMilestoneContent } = this.props - - const extensionDuration = parseInt(value, 10) - - updateMilestoneContent({ - extensionRequest: { - duration: extensionDuration, - } - }) - } - - declineExtension() { - const { updateMilestoneContent } = this.props - - updateMilestoneContent({ - extensionRequest: null, - }) - } - - approveExtension() { - const { extendMilestone, milestone } = this.props - const content = _.get(milestone, 'details.content') - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - - extendMilestone(extensionRequest.duration, { - details: { - ...milestone.details, - content: { - ...content, - extensionRequest: null, - } - } - }) - } - updatedUrl(values, linkIndex) { const { milestone, updateMilestoneContent } = this.props @@ -233,15 +180,15 @@ class MilestoneTypeCheckpointReview extends React.Component { selectedLinks, isSelectWarningVisible, isRejectedExpanded, - isShowExtensionRequestMessage, isShowCompleteConfirmMessage, - isShowExtensionConfirmMessage, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, } = this.state const links = _.get(milestone, 'details.content.links', []) const rejectedLinks = _.reject(links, { isSelected: true }) const isInReview = _.get(milestone, 'details.content.isInReview', false) - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') const isActive = milestone.status === MILESTONE_STATUS.ACTIVE const isCompleted = milestone.status === MILESTONE_STATUS.COMPLETED @@ -354,35 +301,18 @@ class MilestoneTypeCheckpointReview extends React.Component {
)} - {isShowExtensionRequestMessage && ( + {!!extensionRequestDialog && (
- + {extensionRequestDialog}
)} - {!!extensionRequest && ( + {!!extensionRequestConfirmation && (
-
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} - buttons={[ - { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, - { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, - ]} - /> + {extensionRequestConfirmation}
)} @@ -406,8 +336,7 @@ class MilestoneTypeCheckpointReview extends React.Component { { !isCompleted && - !isShowExtensionRequestMessage && - !isShowExtensionConfirmMessage && + !extensionRequestDialog && !isShowCompleteConfirmMessage && (!currentUser.isCustomer || isInReview) && ( @@ -426,14 +355,7 @@ class MilestoneTypeCheckpointReview extends React.Component { }) )} - {!currentUser.isCustomer && !extensionRequest && ( - - )} + {!currentUser.isCustomer && extensionRequestButton}
)} @@ -481,10 +403,12 @@ MilestoneTypeCheckpointReview.defaultProps = { MilestoneTypeCheckpointReview.propTypes = { completeMilestone: PT.func.isRequired, currentUser: PT.object.isRequired, - extendMilestone: PT.func.isRequired, milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeCheckpointReview +export default withMilestoneExtensionRequest(MilestoneTypeCheckpointReview) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReviewOnly/MilestoneTypeCheckpointReviewOnly.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReviewOnly/MilestoneTypeCheckpointReviewOnly.jsx index a06696190..dfcf4871c 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReviewOnly/MilestoneTypeCheckpointReviewOnly.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeCheckpointReviewOnly/MilestoneTypeCheckpointReviewOnly.jsx @@ -11,6 +11,7 @@ import DotIndicator from '../../DotIndicator' import LinkList from '../../LinkList' import MilestonePostMessage from '../../MilestonePostMessage' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS, @@ -27,8 +28,6 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { selectedLinks: [], isInReview: false, isSelectWarningVisible: false, - isShowExtensionRequestMessage: false, - isShowExtensionConfirmMessage: false, isShowCompleteConfirmMessage: false, } @@ -39,11 +38,6 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { this.hideCompleteReviewConfirmation = this.hideCompleteReviewConfirmation.bind(this) this.completeReview = this.completeReview.bind(this) this.toggleRejectedSection = this.toggleRejectedSection.bind(this) - this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) - this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) - this.requestExtension = this.requestExtension.bind(this) - this.approveExtension = this.approveExtension.bind(this) - this.declineExtension = this.declineExtension.bind(this) } showCompleteReviewConfirmation() { @@ -103,53 +97,6 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { }) } - showExtensionRequestMessage() { - this.setState({ - isShowExtensionRequestMessage: true, - isSelectWarningVisible: false, - }) - } - - hideExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: false }) - } - - requestExtension(value) { - const { updateMilestoneContent } = this.props - - const extensionDuration = parseInt(value, 10) - - updateMilestoneContent({ - extensionRequest: { - duration: extensionDuration, - } - }) - } - - declineExtension() { - const { updateMilestoneContent } = this.props - - updateMilestoneContent({ - extensionRequest: null, - }) - } - - approveExtension() { - const { extendMilestone, milestone } = this.props - const content = _.get(milestone, 'details.content') - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - - extendMilestone(extensionRequest.duration, { - details: { - ...milestone.details, - content: { - ...content, - extensionRequest: null, - } - } - }) - } - updatedUrl(values, linkIndex) { const { milestone, updateMilestoneContent } = this.props @@ -218,14 +165,15 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { milestone, theme, currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, } = this.props const { selectedLinks, isSelectWarningVisible, isRejectedExpanded, - isShowExtensionRequestMessage, isShowCompleteConfirmMessage, - isShowExtensionConfirmMessage, } = this.state const isActive = milestone.status === MILESTONE_STATUS.ACTIVE @@ -235,7 +183,6 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { ? _.get(milestone, 'details.content.links', []) : _.get(milestone, 'details.prevMilestoneContent.links', []) const rejectedLinks = _.reject(links, { isSelected: true }) - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') const minCheckedDesigns = this.getMinSelectedDesigns() @@ -282,35 +229,18 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { )} - {isShowExtensionRequestMessage && ( + {!!extensionRequestDialog && (
- + {extensionRequestDialog}
)} - {!!extensionRequest && ( + {!!extensionRequestConfirmation && (
-
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} - buttons={[ - { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, - { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, - ]} - /> + {extensionRequestConfirmation}
)} @@ -334,8 +264,7 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { { !isCompleted && - !isShowExtensionRequestMessage && - !isShowExtensionConfirmMessage && + !extensionRequestDialog && !isShowCompleteConfirmMessage && ( @@ -350,14 +279,7 @@ class MilestoneTypeCheckpointReviewOnly extends React.Component { : `${-hoursLeft}h delay` }) - {!currentUser.isCustomer && !extensionRequest && ( - - )} + {!currentUser.isCustomer && extensionRequestButton}
)} @@ -405,10 +327,12 @@ MilestoneTypeCheckpointReviewOnly.defaultProps = { MilestoneTypeCheckpointReviewOnly.propTypes = { completeMilestone: PT.func.isRequired, currentUser: PT.object.isRequired, - extendMilestone: PT.func.isRequired, milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeCheckpointReviewOnly +export default withMilestoneExtensionRequest(MilestoneTypeCheckpointReviewOnly) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesigns/MilestoneTypeFinalDesigns.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesigns/MilestoneTypeFinalDesigns.jsx index d6ea93a61..84a697dbb 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesigns/MilestoneTypeFinalDesigns.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesigns/MilestoneTypeFinalDesigns.jsx @@ -13,6 +13,7 @@ import MilestonePostMessage from '../../MilestonePostMessage' import ProjectProgress from '../../../ProjectProgress' import WinnerSelectionBar from '../../WinnerSelectionBar' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS, @@ -29,8 +30,6 @@ class MilestoneTypeFinalDesigns extends React.Component { selectedLinks: [], places: [-1, -1, -1], isInReview: false, - isShowExtensionRequestMessage: false, - isShowExtensionConfirmMessage: false, isShowCompleteConfirmMessage: false, isShowCustomerCompleteConfirmMessage: false, } @@ -42,11 +41,6 @@ class MilestoneTypeFinalDesigns extends React.Component { this.showCustomerCompleteReviewConfirmation = this.showCustomerCompleteReviewConfirmation.bind(this) this.hideCustomerCompleteReviewConfirmation = this.hideCustomerCompleteReviewConfirmation.bind(this) this.completeReview = this.completeReview.bind(this) - this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) - this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) - this.requestExtension = this.requestExtension.bind(this) - this.approveExtension = this.approveExtension.bind(this) - this.declineExtension = this.declineExtension.bind(this) this.moveToReviewingState = this.moveToReviewingState.bind(this) this.onBonusChange = this.onBonusChange.bind(this) this.onPlaceChange = this.onPlaceChange.bind(this) @@ -123,50 +117,6 @@ class MilestoneTypeFinalDesigns extends React.Component { return Math.min(links.length, MIN_WINNER_DESIGNS) } - showExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: true }) - } - - hideExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: false }) - } - - requestExtension(value) { - const { updateMilestoneContent } = this.props - - const extensionDuration = parseInt(value, 10) - - updateMilestoneContent({ - extensionRequest: { - duration: extensionDuration, - } - }) - } - - declineExtension() { - const { updateMilestoneContent } = this.props - - updateMilestoneContent({ - extensionRequest: null, - }) - } - - approveExtension() { - const { extendMilestone, milestone } = this.props - const content = _.get(milestone, 'details.content') - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - - extendMilestone(extensionRequest.duration, { - details: { - ...milestone.details, - content: { - ...content, - extensionRequest: null, - } - } - }) - } - updatedUrl(values, linkIndex) { const { milestone, updateMilestoneContent } = this.props @@ -260,20 +210,20 @@ class MilestoneTypeFinalDesigns extends React.Component { milestone, theme, currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, } = this.props const { selectedLinks, isSelectWarningVisible, isShowCustomerCompleteConfirmMessage, - isShowExtensionRequestMessage, isShowCompleteConfirmMessage, - isShowExtensionConfirmMessage, places, } = this.state const links = _.get(milestone, 'details.content.links', []) const isInReview = _.get(milestone, 'details.content.isInReview', false) - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') const isActive = milestone.status === MILESTONE_STATUS.ACTIVE const isCompleted = milestone.status === MILESTONE_STATUS.COMPLETED @@ -397,10 +347,26 @@ class MilestoneTypeFinalDesigns extends React.Component { )} + {!!extensionRequestDialog && ( + +
+ {extensionRequestDialog} +
+
+ )} + + {!!extensionRequestConfirmation && ( + +
+ {extensionRequestConfirmation} +
+
+ )} + { !isCompleted && - !isShowExtensionRequestMessage && - !isShowExtensionConfirmMessage && + !extensionRequestConfirmation && + !extensionRequestDialog && !isShowCompleteConfirmMessage && !isShowCustomerCompleteConfirmMessage && (!currentUser.isCustomer || isInReview) && @@ -420,47 +386,7 @@ class MilestoneTypeFinalDesigns extends React.Component { }) )} - {!currentUser.isCustomer && !extensionRequest && ( - - )} - - - )} - - {isShowExtensionRequestMessage && ( - -
- -
-
- )} - - {!!extensionRequest && ( - -
-
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} - buttons={[ - { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, - { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, - ]} - /> + {!currentUser.isCustomer && extensionRequestButton}
)} @@ -538,10 +464,12 @@ MilestoneTypeFinalDesigns.defaultProps = { MilestoneTypeFinalDesigns.propTypes = { completeMilestone: PT.func.isRequired, currentUser: PT.object.isRequired, - extendMilestone: PT.func.isRequired, milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeFinalDesigns +export default withMilestoneExtensionRequest(MilestoneTypeFinalDesigns) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesignsSelectionOnly/MilestoneTypeFinalDesignsSelectionOnly.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesignsSelectionOnly/MilestoneTypeFinalDesignsSelectionOnly.jsx index 49820e74d..be4f34826 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesignsSelectionOnly/MilestoneTypeFinalDesignsSelectionOnly.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalDesignsSelectionOnly/MilestoneTypeFinalDesignsSelectionOnly.jsx @@ -11,6 +11,7 @@ import DotIndicator from '../../DotIndicator' import MilestonePostMessage from '../../MilestonePostMessage' import WinnerSelectionBar from '../../WinnerSelectionBar' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS, @@ -26,8 +27,6 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { this.state = { selectedLinks: [], places: [-1, -1, -1], - isShowExtensionRequestMessage: false, - isShowExtensionConfirmMessage: false, isShowCompleteConfirmMessage: false, isShowCustomerCompleteConfirmMessage: false, } @@ -37,11 +36,6 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { this.showCustomerCompleteSelectionConfirmation = this.showCustomerCompleteSelectionConfirmation.bind(this) this.hideCustomerCompleteSelectionConfirmation = this.hideCustomerCompleteSelectionConfirmation.bind(this) this.completeSelection = this.completeSelection.bind(this) - this.showExtensionRequestMessage = this.showExtensionRequestMessage.bind(this) - this.hideExtensionRequestMessage = this.hideExtensionRequestMessage.bind(this) - this.requestExtension = this.requestExtension.bind(this) - this.approveExtension = this.approveExtension.bind(this) - this.declineExtension = this.declineExtension.bind(this) this.onBonusChange = this.onBonusChange.bind(this) this.onPlaceChange = this.onPlaceChange.bind(this) } @@ -117,50 +111,6 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { return Math.min(links.length, MIN_WINNER_DESIGNS) } - showExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: true }) - } - - hideExtensionRequestMessage() { - this.setState({ isShowExtensionRequestMessage: false }) - } - - requestExtension(value) { - const { updateMilestoneContent } = this.props - - const extensionDuration = parseInt(value, 10) - - updateMilestoneContent({ - extensionRequest: { - duration: extensionDuration, - } - }) - } - - declineExtension() { - const { updateMilestoneContent } = this.props - - updateMilestoneContent({ - extensionRequest: null, - }) - } - - approveExtension() { - const { extendMilestone, milestone } = this.props - const content = _.get(milestone, 'details.content') - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') - - extendMilestone(extensionRequest.duration, { - details: { - ...milestone.details, - content: { - ...content, - extensionRequest: null, - } - } - }) - } - onBonusChange(linkIndex, isSelected) { const { selectedLinks } = this.state @@ -213,19 +163,19 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { milestone, theme, currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, } = this.props const { selectedLinks, isSelectWarningVisible, isShowCustomerCompleteConfirmMessage, - isShowExtensionRequestMessage, isShowCompleteConfirmMessage, - isShowExtensionConfirmMessage, places, } = this.state - + const links = _.get(milestone, 'details.prevMilestoneContent.links', []) - const extensionRequest = _.get(milestone, 'details.content.extensionRequest') const isActive = milestone.status === MILESTONE_STATUS.ACTIVE const isCompleted = milestone.status === MILESTONE_STATUS.COMPLETED @@ -285,13 +235,28 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { )} + {!!extensionRequestDialog && ( + +
+ {extensionRequestDialog} +
+
+ )} + + {!!extensionRequestConfirmation && ( + +
+ {extensionRequestConfirmation} +
+
+ )} + { !isCompleted && - !isShowExtensionRequestMessage && - !isShowExtensionConfirmMessage && + !extensionRequestDialog && !isShowCompleteConfirmMessage && !isShowCustomerCompleteConfirmMessage && - (!currentUser.isCustomer) && + !currentUser.isCustomer && (
@@ -307,47 +272,7 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { }) )} - {!currentUser.isCustomer && !extensionRequest && ( - - )} -
-
- )} - - {isShowExtensionRequestMessage && ( - -
- -
-
- )} - - {!!extensionRequest && ( - -
-
Please make a decision in the next 24h. After that we will automatically extend the project to make sure we deliver success to you.`} - buttons={[ - { title: 'Decline extension', onClick: this.declineExtension, type: 'warning' }, - { title: 'Approve extension', onClick: this.approveExtension, type: 'primary' }, - ]} - /> + {!currentUser.isCustomer && extensionRequestButton}
)} @@ -399,7 +324,7 @@ class MilestoneTypeFinalDesignsSelectionOnly extends React.Component { - {_.get(milestone, 'details.content.links', []).filter((link) => + {_.get(milestone, 'details.content.links', []).filter((link) => (link.selectedPlace || link.isSelected)).map((link, index) => (
0 ? (totalDays - daysLeft) / totalDays * 100 : 100 - + return (
- + @@ -113,9 +123,8 @@ class MilestoneTypeFinalFixes extends React.Component { {finalFixRequests.map((finalFixRequest, index) => ( -
+
@@ -138,7 +147,31 @@ class MilestoneTypeFinalFixes extends React.Component { isUpdating={milestone.isUpdating} canAddLink /> + + )} + {isActive && !!extensionRequestDialog && ( + +
+ {extensionRequestDialog} +
+
+ )} + + {isActive && !!extensionRequestConfirmation && ( + +
+ {extensionRequestConfirmation} +
+
+ )} + + { + isActive && + !extensionRequestDialog && + !currentUser.isCustomer && + ( +
+ {extensionRequestButton}
@@ -162,8 +196,13 @@ MilestoneTypeFinalFixes.defaultProps = { } MilestoneTypeFinalFixes.propTypes = { + completeFinalFixesMilestone: PT.func.isRequired, theme: PT.string, milestone: PT.object.isRequired, + updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeFinalFixes +export default withMilestoneExtensionRequest(MilestoneTypeFinalFixes) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalFixes/MilestoneTypeFinalFixes.scss b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalFixes/MilestoneTypeFinalFixes.scss index abbe44a6d..3e4e27598 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalFixes/MilestoneTypeFinalFixes.scss +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeFinalFixes/MilestoneTypeFinalFixes.scss @@ -20,5 +20,9 @@ display: flex; justify-content: center; padding: 2 * $base-unit; + + > button + button { + margin-left: 2 * $base-unit; + } } } diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.jsx index e0c76fb74..50b8f291c 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.jsx @@ -9,6 +9,7 @@ import cn from 'classnames' import DotIndicator from '../../DotIndicator' import LinkList from '../../LinkList' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS } from '../../../../../../config/constants' @@ -70,7 +71,14 @@ class MilestoneTypePhaseSpecification extends React.Component { } render() { - const { milestone, theme, currentUser } = this.props + const { + milestone, + theme, + currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, + } = this.props const links = _.get(milestone, 'details.content.links', []) const isActive = milestone.status === MILESTONE_STATUS.ACTIVE @@ -90,37 +98,57 @@ class MilestoneTypePhaseSpecification extends React.Component { {isActive && (
{!currentUser.isCustomer && ( -
- - - - - -
-
- -
+ + + + )} + + {!!extensionRequestDialog && ( + +
+ {extensionRequestDialog} +
+
+ )} + + {!!extensionRequestConfirmation && ( + +
+ {extensionRequestConfirmation} +
+
+ )} + + { + !currentUser.isCustomer && + !extensionRequestDialog && + ( + +
+
+ + {!currentUser.isCustomer && extensionRequestButton}
- -
+
+
)}
)} @@ -150,6 +178,9 @@ MilestoneTypePhaseSpecification.propTypes = { milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypePhaseSpecification +export default withMilestoneExtensionRequest(MilestoneTypePhaseSpecification) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.scss b/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.scss index f321601a8..39324dbc8 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.scss +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypePhaseSpecification/MilestoneTypePhaseSpecification.scss @@ -17,5 +17,9 @@ display: flex; justify-content: center; padding: 2 * $base-unit; + + > button + button { + margin-left: 2 * $base-unit; + } } } diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.jsx index 80c4de64a..5300463fe 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.jsx +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.jsx @@ -11,6 +11,7 @@ import DotIndicator from '../../DotIndicator' import ProjectProgress from '../../../ProjectProgress' import LinkList from '../../LinkList' import MilestoneDescription from '../../MilestoneDescription' +import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest' import { MILESTONE_STATUS } from '../../../../../../config/constants' @@ -69,7 +70,14 @@ class MilestoneTypeProgress extends React.Component { } render() { - const { milestone, theme, currentUser } = this.props + const { + milestone, + theme, + currentUser, + extensionRequestDialog, + extensionRequestButton, + extensionRequestConfirmation, + } = this.props const links = _.get(milestone, 'details.content.links', []) const isActive = milestone.status === MILESTONE_STATUS.ACTIVE @@ -101,7 +109,7 @@ class MilestoneTypeProgress extends React.Component { {isActive && (
- + {!currentUser.isCustomer && ( -
- + + )} + {!!extensionRequestDialog && ( +
- -
- -
-
+ {extensionRequestDialog}
-
+
+ )} + + {!!extensionRequestConfirmation && ( + +
+ {extensionRequestConfirmation} +
+
+ )} + + { + !currentUser.isCustomer && + !extensionRequestDialog && + ( + +
+
+ + {!currentUser.isCustomer && extensionRequestButton} +
+
+
)}
)} @@ -167,6 +195,9 @@ MilestoneTypeProgress.propTypes = { milestone: PT.object.isRequired, theme: PT.string, updateMilestoneContent: PT.func.isRequired, + extensionRequestDialog: PT.node, + extensionRequestButton: PT.node, + extensionRequestConfirmation: PT.node, } -export default MilestoneTypeProgress +export default withMilestoneExtensionRequest(MilestoneTypeProgress) diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.scss b/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.scss index f321601a8..39324dbc8 100644 --- a/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.scss +++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeProgress/MilestoneTypeProgress.scss @@ -17,5 +17,9 @@ display: flex; justify-content: center; padding: 2 * $base-unit; + + > button + button { + margin-left: 2 * $base-unit; + } } } diff --git a/src/projects/reducers/productsTimelines.js b/src/projects/reducers/productsTimelines.js index f43e652b2..60476f054 100644 --- a/src/projects/reducers/productsTimelines.js +++ b/src/projects/reducers/productsTimelines.js @@ -87,10 +87,18 @@ function updateMilestone(state, productId, milestoneId, dirtyMilestone, shouldRe * @returns {Object} The state */ function updateTimeline(state, productId, updateTimeline, shouldReplace = false) { + const milestones = _.get(state, `[${productId}].timeline.milestones`) + const updatedTimeline = shouldReplace ? updateTimeline : update(state[productId].timeline, updateTimeline) + // restore milestones inside timeline after replacing timeline + // if udpated timeline doesn't have milestones + if (shouldReplace && milestones && !updateTimeline.milestones) { + updatedTimeline.milestones = milestones + } + return update(state, { [productId]: { timeline: {$set: updatedTimeline} diff --git a/src/projects/reducers/project.js b/src/projects/reducers/project.js index 83d40e6ed..ac9d92894 100644 --- a/src/projects/reducers/project.js +++ b/src/projects/reducers/project.js @@ -171,6 +171,7 @@ export const projectState = function (state=initialState, action) { case CLEAR_LOADED_PROJECT: case GET_PROJECTS_SUCCESS: return Object.assign({}, state, { + isLoading: true, // this is excpected to be default value when there is not project loaded project: {}, projectNonDirty: {}, phases: null, diff --git a/src/projects/reducers/projectDashboard.js b/src/projects/reducers/projectDashboard.js index 0f6a29c75..a59df9cc9 100644 --- a/src/projects/reducers/projectDashboard.js +++ b/src/projects/reducers/projectDashboard.js @@ -2,7 +2,9 @@ import { LOAD_PROJECT_DASHBOARD_PENDING, LOAD_PROJECT_DASHBOARD_SUCCESS, - LOAD_PROJECT_DASHBOARD_FAILURE + LOAD_PROJECT_DASHBOARD_FAILURE, + CLEAR_LOADED_PROJECT, + GET_PROJECTS_SUCCESS, } from '../../config/constants' const initialState = { @@ -30,6 +32,14 @@ export const projectDashboard = function (state=initialState, action) { error: true }) + // when we clear the project we have to put dashboard state to the initial state + // because the code relies on the initial state + // for example spinnerWhileLoading in ProjectDerail.jsx expects `isLoading` to be true + // to prevent components which require dashboard data from rendering + case CLEAR_LOADED_PROJECT: + case GET_PROJECTS_SUCCESS: + return Object.assign({}, state, initialState) + default: return state }