+
@@ -138,7 +147,31 @@ class MilestoneTypeFinalFixes extends React.Component {
isUpdating={milestone.isUpdating}
canAddLink
/>
+
+ )}
+ {isActive && !!extensionRequestDialog && (
+
+
+ {extensionRequestDialog}
+
+
+ )}
+
+ {isActive && !!extensionRequestConfirmation && (
+
+
+ {extensionRequestConfirmation}
+
+
+ )}
+
+ {
+ isActive &&
+ !extensionRequestDialog &&
+ !currentUser.isCustomer &&
+ (
+
Mark as completed
+ {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 && (
-
-
-
-
-
-
-
-
-
- Mark as completed
-
-
+
+
+
+ )}
+
+ {!!extensionRequestDialog && (
+
+
+ {extensionRequestDialog}
+
+
+ )}
+
+ {!!extensionRequestConfirmation && (
+
+
+ {extensionRequestConfirmation}
+
+
+ )}
+
+ {
+ !currentUser.isCustomer &&
+ !extensionRequestDialog &&
+ (
+
+
+
+
+ Mark as completed
+
+ {!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 && (
+
-
-
-
- Mark as completed
-
-
-
+ {extensionRequestDialog}
-
+
+ )}
+
+ {!!extensionRequestConfirmation && (
+
+
+ {extensionRequestConfirmation}
+
+
+ )}
+
+ {
+ !currentUser.isCustomer &&
+ !extensionRequestDialog &&
+ (
+
+
+
+
+ Mark as completed
+
+ {!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
}