diff --git a/src/projects/actions/productsTimelines.js b/src/projects/actions/productsTimelines.js
index 25e44d253..935ed8e80 100644
--- a/src/projects/actions/productsTimelines.js
+++ b/src/projects/actions/productsTimelines.js
@@ -29,6 +29,7 @@ import {
BULK_UPDATE_PRODUCT_MILESTONES,
} from '../../config/constants'
import { processUpdateMilestone } from '../../helpers/milestoneHelper'
+import moment from 'moment'
/**
* Get the next milestone in the list, which is not hidden
@@ -448,3 +449,79 @@ export function completeFinalFixesMilestone(productId, timelineId, milestoneId,
})
}
}
+
+/**
+ * Mark the milestone as 'completed' and append a 'deliverable-final-fixes' milestone after it
+ * @param {Number} productId product id
+ * @param {Number} timelineId timeline id
+ * @param {Number} milestoneId milestone id
+ * @param {String} finalFixRequest final fixes request text
+ */
+export function submitDeliverableFinalFixesRequest(productId, timelineId, milestoneId, finalFixRequest) {
+ return (dispatch, getState) => {
+ const state = getState()
+ const timeline = state.productsTimelines[productId].timeline
+ const milestoneIdx = _.findIndex(timeline.milestones, { id: milestoneId })
+ const milestone = timeline.milestones[milestoneIdx]
+
+ let updatedTimelineMilestones = [
+ ...timeline.milestones,
+ ]
+
+ updatedTimelineMilestones = processUpdateMilestone(
+ milestone, {
+ status: MILESTONE_STATUS.COMPLETED,
+ }, updatedTimelineMilestones
+ ).updatedTimelineMilestones
+
+ const finalFixesMilestone = {
+ type: 'deliverable-final-fixes',
+ startDate: milestone.endDate,
+ endDate: moment(milestone.endDate).add(3, 'day').toISOString(),
+ status: MILESTONE_STATUS.ACTIVE,
+ details: {
+ content: {
+ finalFixesRequest: finalFixRequest,
+ }
+ },
+ name: 'Final Fixes',
+ duration: 3,
+ order: timeline.milestones.length + 1,
+ hidden: false,
+ completedText: 'completed text',
+ activeText: 'active text',
+ description: 'description',
+ plannedText: 'planned text',
+ blockedText: 'blocked text',
+ }
+
+ updatedTimelineMilestones.splice(milestoneIdx + 1, 0, finalFixesMilestone)
+
+ dispatch({
+ type: SUBMIT_FINAL_FIXES_REQUEST_PENDING,
+ meta: { productId, milestoneId }
+ })
+
+ const milestones = updatedTimelineMilestones.map(milestone => _.omit(milestone, ['timelineId', 'error', 'isUpdating', 'statusHistory']))
+
+ return dispatch({
+ type: BULK_UPDATE_PRODUCT_MILESTONES,
+ payload: updateMilestones(timelineId, milestones),
+ meta: {
+ productId,
+ }
+ }).then(() => {
+ dispatch({
+ type: SUBMIT_FINAL_FIXES_REQUEST_SUCCESS,
+ meta: { productId, milestoneId }
+ })
+ }).catch((error) => {
+ dispatch({
+ type: SUBMIT_FINAL_FIXES_REQUEST_FAILURE,
+ payload: error,
+ meta: { productId, milestoneId }
+ })
+ throw error
+ })
+ }
+}
diff --git a/src/projects/detail/components/timeline/Milestone/Milestone.jsx b/src/projects/detail/components/timeline/Milestone/Milestone.jsx
index e92e9cd0c..4e91be096 100644
--- a/src/projects/detail/components/timeline/Milestone/Milestone.jsx
+++ b/src/projects/detail/components/timeline/Milestone/Milestone.jsx
@@ -21,6 +21,8 @@ import MilestoneTypeFinalDesigns from '../milestones/MilestoneTypeFinalDesigns'
import MilestoneTypeDelivery from '../milestones/MilestoneTypeDelivery'
import MilestoneTypeFinalFixes from '../milestones/MilestoneTypeFinalFixes'
import MilestoneTypeAddLinks from '../milestones/MilestoneTypeAddLinks'
+import MilestoneTypeReporting from '../milestones/MilestoneTypeReporting'
+import MilestoneTypeDeliverableReview from '../milestones/MilestoneTypeDeliverableReview'
import DotIndicator from '../DotIndicator'
import MobilePage from '../../../../../components/MobilePage/MobilePage'
import MediaQuery from 'react-responsive'
@@ -48,6 +50,7 @@ class Milestone extends React.Component {
this.completeFinalFixesMilestone = this.completeFinalFixesMilestone.bind(this)
this.extendMilestone = this.extendMilestone.bind(this)
this.submitFinalFixesRequest = this.submitFinalFixesRequest.bind(this)
+ this.submitDeliverableFinalFixesRequest = this.submitDeliverableFinalFixesRequest.bind(this)
this.milestoneEditorChanged = this.milestoneEditorChanged.bind(this)
this.state = {
@@ -157,7 +160,7 @@ class Milestone extends React.Component {
}
}
- updateMilestoneContent(contentProps, metaDataProps) {
+ updateMilestoneContent(contentProps, metaDataProps, status) {
const { updateMilestone, milestone } = this.props
const updatedMilestone = {
@@ -174,6 +177,10 @@ class Milestone extends React.Component {
}
}
+ if (status) {
+ updatedMilestone.status = status
+ }
+
updateMilestone(milestone.id, updatedMilestone)
}
@@ -216,6 +223,12 @@ class Milestone extends React.Component {
submitFinalFixesRequest(milestone.id, finalFixRequests)
}
+ submitDeliverableFinalFixesRequest(finalFixesRequest) {
+ const { submitDeliverableFinalFixesRequest, milestone } = this.props
+
+ submitDeliverableFinalFixesRequest(milestone.id, finalFixesRequest)
+ }
+
render() {
const {
milestone,
@@ -330,6 +343,7 @@ class Milestone extends React.Component {
disableSubmitButton={this.state.disableSubmit}
/>
)
+
return (
{(
)}
@@ -491,6 +505,24 @@ class Milestone extends React.Component {
/>
)
}
+
+ {!isEditing && !isUpdating && milestone.type === 'reporting' && (
+
+ )}
+
+ {!isEditing && !isUpdating && (milestone.type === 'deliverable-review' || milestone.type === 'final-deliverable-review' || milestone.type === 'deliverable-final-fixes') && (
+
+ )}
)
@@ -504,6 +536,7 @@ Milestone.propTypes = {
milestone: PT.object.isRequired,
submitFinalFixesRequest: PT.func.isRequired,
updateMilestone: PT.func.isRequired,
+ submitDeliverableFinalFixesRequest: PT.func.isRequired,
}
export default Milestone
diff --git a/src/projects/detail/components/timeline/Timeline/Timeline.jsx b/src/projects/detail/components/timeline/Timeline/Timeline.jsx
index 12ed9cbb8..96021dbb9 100644
--- a/src/projects/detail/components/timeline/Timeline/Timeline.jsx
+++ b/src/projects/detail/components/timeline/Timeline/Timeline.jsx
@@ -25,6 +25,7 @@ class Timeline extends React.Component {
this.completeFinalFixesMilestone = this.completeFinalFixesMilestone.bind(this)
this.extendMilestone = this.extendMilestone.bind(this)
this.submitFinalFixesRequest = this.submitFinalFixesRequest.bind(this)
+ this.submitDeliverableFinalFixesRequest = this.submitDeliverableFinalFixesRequest.bind(this)
}
componentWillReceiveProps() {
@@ -91,6 +92,16 @@ class Timeline extends React.Component {
submitFinalFixesRequest(product.id, timeline.id, milestoneId, finalFixRequests)
}
+ submitDeliverableFinalFixesRequest(milestoneId, finalFixesRequest) {
+ const {
+ product,
+ submitDeliverableFinalFixesRequest,
+ timeline,
+ } = this.props
+
+ submitDeliverableFinalFixesRequest(product.id, timeline.id, milestoneId, finalFixesRequest)
+ }
+
render() {
const {
currentUser,
@@ -123,6 +134,7 @@ class Timeline extends React.Component {
extendMilestone={this.extendMilestone}
submitFinalFixesRequest={this.submitFinalFixesRequest}
completeFinalFixesMilestone={this.completeFinalFixesMilestone}
+ submitDeliverableFinalFixesRequest={this.submitDeliverableFinalFixesRequest}
//$TODO convert the below logic more optimized way
previousMilestone={_.find(orderedMilestones, m => m.order === milestone.order-1) &&
_.find(orderedMilestones, m => m.order === milestone.order-1).type}
@@ -146,6 +158,7 @@ Timeline.propType = {
updateProductMilestone: PT.func.isRequired,
completeProductMilestone: PT.func.isRequired,
extendProductMilestone: PT.func.isRequired,
+ submitDeliverableFinalFixesRequest: PT.func.isRequired,
}
export default Timeline
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.jsx
new file mode 100644
index 000000000..79e322207
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.jsx
@@ -0,0 +1,368 @@
+/**
+ * Milestone type `deliverable-review`, `final-deliverable-review`, `deliverable-final-fixes`
+ */
+import React from 'react'
+import PT from 'prop-types'
+import _ from 'lodash'
+import cn from 'classnames'
+
+import DotIndicator from '../../DotIndicator'
+import LinkList from '../../LinkList'
+import LinkItem from '../../LinkItem'
+import LinkItemForm from '../../LinkItemForm'
+import MilestoneDescription from '../../MilestoneDescription'
+import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest'
+import { getMilestoneStatusText } from '../../../../../../helpers/milestoneHelper'
+
+import {
+ MILESTONE_STATUS
+} from '../../../../../../config/constants'
+
+import './MilestoneTypeDeliverableReview.scss'
+import { hasPermission } from '../../../../../../helpers/permissions'
+import { PERMISSIONS } from '../../../../../../config/permissions'
+
+class MilestoneTypeDeliverableReview extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ isAddingDeliverableUpdate: false,
+ isAddingFeedbackLink: false,
+ deliverableUpdateText: '',
+ feedbackLink: '',
+ isAdddingFinalFixesRequest: false,
+ finalFixesRequest: '',
+ }
+
+ this.onClickAddDeliverableReview = this.onClickAddDeliverableReview.bind(this)
+ this.onClickAddFeedbackLink = this.onClickAddFeedbackLink.bind(this)
+ this.onDeliverableUpdateTextChange = this.onDeliverableUpdateTextChange.bind(this)
+ this.onClickSaveDeliverableUpdate = this.onClickSaveDeliverableUpdate.bind(this)
+ this.onClickCancelAddDeliverableUpdate = this.onClickCancelAddDeliverableUpdate.bind(this)
+ this.updatedSubmissionUrl = this.updatedSubmissionUrl.bind(this)
+ this.removeSubmissionUrl = this.removeSubmissionUrl.bind(this)
+ this.onSubmitFeedbackLink = this.onSubmitFeedbackLink.bind(this)
+ this.onAddFeedbackLinkCancel = this.onAddFeedbackLinkCancel.bind(this)
+ this.onDeleteFeedbackLink = this.onDeleteFeedbackLink.bind(this)
+ this.onClickReviewComplete = this.onClickReviewComplete.bind(this)
+
+ this.onClickRequestFixes = this.onClickRequestFixes.bind(this)
+ this.onClickCancelFinalFixesRequest = this.onClickCancelFinalFixesRequest.bind(this)
+ this.onClickSubmitRequest = this.onClickSubmitRequest.bind(this)
+ this.onFinalFixesRequestTextChange = this.onFinalFixesRequestTextChange.bind(this)
+ }
+
+ onClickAddDeliverableReview() {
+ const { milestone } = this.props
+
+ this.setState({
+ isAddingDeliverableUpdate: true,
+ deliverableUpdateText: _.get(milestone, 'details.content.deliverableUpdate'),
+ })
+ }
+
+ onClickAddFeedbackLink() {
+ const { milestone } = this.props
+
+ this.setState({
+ isAddingFeedbackLink: true,
+ feedbackLink: _.get(milestone, 'details.content.feedbackLink'),
+ })
+ }
+
+ onAddFeedbackLinkCancel() {
+ this.setState({
+ isAddingFeedbackLink: false,
+ })
+ }
+
+ onClickCancelAddDeliverableUpdate() {
+ this.setState({
+ isAddingDeliverableUpdate: false,
+ })
+ }
+
+ onDeliverableUpdateTextChange(event) {
+ this.setState({
+ deliverableUpdateText: event.target.value,
+ })
+ }
+
+ onClickSaveDeliverableUpdate() {
+ const { deliverableUpdateText } = this.state
+ const { updateMilestoneContent } = this.props
+
+ updateMilestoneContent({ deliverableUpdate: deliverableUpdateText })
+ }
+
+ updatedSubmissionUrl(values, linkIndex) {
+ const { milestone, updateMilestoneContent } = this.props
+
+ const submissionLinks = [..._.get(milestone, 'details.content.submissionLinks', [])]
+
+ values.type = 'marvelapp'
+
+ if (typeof linkIndex === 'number') {
+ submissionLinks.splice(linkIndex, 1, values)
+ } else {
+ submissionLinks.push(values)
+ }
+
+ updateMilestoneContent({
+ submissionLinks
+ })
+ }
+
+ removeSubmissionUrl(linkIndex) {
+ if (!window.confirm('Are you sure you want to remove this link?')) {
+ return
+ }
+
+ const { milestone, updateMilestoneContent } = this.props
+ const submissionLinks = [..._.get(milestone, 'details.content.submissionLinks', [])]
+
+ submissionLinks.splice(linkIndex, 1)
+
+ updateMilestoneContent({
+ submissionLinks
+ })
+ }
+
+ onDeleteFeedbackLink() {
+ if (!window.confirm('Are you sure you want to remove this link?')) {
+ return
+ }
+
+ const { updateMilestoneContent } = this.props
+
+ updateMilestoneContent({
+ feedbackLink: '',
+ })
+ }
+
+ onSubmitFeedbackLink({ url }) {
+ const { updateMilestoneContent } = this.props
+
+ updateMilestoneContent({
+ feedbackLink: url,
+ })
+ }
+
+ onClickReviewComplete() {
+ const { completeMilestone } = this.props
+
+ completeMilestone()
+ }
+
+ onClickRequestFixes() {
+ this.setState({
+ isAdddingFinalFixesRequest: true,
+ })
+ }
+
+ onClickCancelFinalFixesRequest() {
+ this.setState({
+ isAdddingFinalFixesRequest: false,
+ })
+ }
+
+ onClickSubmitRequest() {
+ const { submitDeliverableFinalFixesRequest } = this.props
+ const { finalFixesRequest } = this.state
+
+ submitDeliverableFinalFixesRequest(finalFixesRequest)
+ }
+
+ onFinalFixesRequestTextChange(event) {
+ this.setState({
+ finalFixesRequest: event.target.value,
+ })
+ }
+
+ render() {
+ const {
+ milestone,
+ theme,
+ } = this.props
+ const {
+ isAddingDeliverableUpdate,
+ isAddingFeedbackLink,
+ deliverableUpdateText,
+ feedbackLink,
+
+ isAdddingFinalFixesRequest,
+ finalFixesRequest,
+ } = this.state
+
+ const isActive = milestone.status === MILESTONE_STATUS.ACTIVE
+
+ const canManage = hasPermission(PERMISSIONS.MANAGE_MILESTONE)
+ const canAcceptFinalDelivery = hasPermission(PERMISSIONS.ACCEPT_MILESTONE_FINAL_DELIVERY)
+
+ const milestoneDeliverableFinalFixesRequest = _.get(milestone, 'details.content.finalFixesRequest', '')
+ const milestoneDeliverableUpdate = _.get(milestone, 'details.content.deliverableUpdate', '')
+ const milestoneFeedbackLink = _.get(milestone, 'details.content.feedbackLink', '')
+ const milestoneSubmissionLinks = _.get(milestone, 'details.content.submissionLinks', [])
+
+ return (
+
+
+
+
+
+
+
+
+ {/* For milestone type of 'deliverable-final-fixes' specifically */}
+ {milestone.type === 'deliverable-final-fixes' && (
+
+
{milestoneDeliverableFinalFixesRequest}
+
+ )}
+
+ {/* Deliverable update */}
+ {isAddingDeliverableUpdate ? (
+
+ ) : (
+ milestoneDeliverableUpdate && (
+
+
{milestoneDeliverableUpdate}
+ {
+ canManage && (
+
+
+
+ )
+ }
+
+ ) || (
+ isActive && canManage && (
+
+ )
+ )
+ )}
+
+ {/* Feedback link */}
+ {milestone.type !== 'deliverable-final-fixes' && (
+ isAddingFeedbackLink ? (
+
+
+
+ ) : (
+ milestoneFeedbackLink && (
+
+
+
+ ) || (
+ isActive && canManage && (
+
+ )
+ )
+ )
+ )}
+
+ {/* Submission link */}
+
+
+
+
+
+ {/* For milestone types other than 'final-deliverable-review' specifically */}
+ {milestone.type !== 'final-deliverable-review' && (
+ isActive && canAcceptFinalDelivery && (
+
+
+
+ )
+ )}
+
+ {/* For milestone type of 'final-deliverable-review' specifically */}
+ {milestone.type === 'final-deliverable-review' && (
+ isActive && canAcceptFinalDelivery && (
+ isAdddingFinalFixesRequest ? (
+
+ ) : (
+
+
+
+
+ )
+ )
+ )}
+
+
+
+ )
+ }
+}
+
+MilestoneTypeDeliverableReview.defaultProps = {
+ theme: null,
+}
+
+MilestoneTypeDeliverableReview.propTypes = {
+ milestone: PT.object,
+ theme: PT.string,
+ updateMilestoneContent: PT.func.isRequired,
+ submitDeliverableFinalFixesRequest: PT.func.isRequired,
+ completeMilestone: PT.func.isRequired,
+}
+
+export default withMilestoneExtensionRequest(MilestoneTypeDeliverableReview)
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.scss b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.scss
new file mode 100644
index 000000000..a8b5f9766
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/MilestoneTypeDeliverableReview.scss
@@ -0,0 +1,40 @@
+@import '../../../../../../styles/includes';
+@import '~tc-ui/src/styles/tc-includes';
+
+.milestone-post {
+ @include roboto;
+ font-size: 15px;
+
+ .btns-container {
+ margin-top: 8px;
+ > *:not(:first-child) {
+ margin-left: 8px;
+ }
+ }
+
+ .btns-container-vertical {
+ margin-top: 8px;
+ > *:not(:first-child) {
+ margin-top: 8px;
+ }
+ }
+
+ .fullwidth {
+ width: 100%;
+ }
+
+ .report-textarea {
+ height: unset;
+ margin-top: 8px;
+ }
+
+ .view-report-text {
+ margin-top: 8px;
+ white-space: pre-wrap;
+ }
+
+ .review-complete-btn-container {
+ margin-top: 16px;
+ }
+
+}
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/index.js b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/index.js
new file mode 100644
index 000000000..3cac7458d
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeDeliverableReview/index.js
@@ -0,0 +1,2 @@
+import MilestoneTypeDeliverableReview from './MilestoneTypeDeliverableReview'
+export default MilestoneTypeDeliverableReview
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.jsx b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.jsx
new file mode 100644
index 000000000..a6f6a03cb
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.jsx
@@ -0,0 +1,155 @@
+/**
+ * Milestone type 'reporting`
+ */
+import React from 'react'
+import PT from 'prop-types'
+import _ from 'lodash'
+import cn from 'classnames'
+
+import DotIndicator from '../../DotIndicator'
+import MilestoneDescription from '../../MilestoneDescription'
+import { withMilestoneExtensionRequest } from '../../MilestoneExtensionRequest'
+import { getMilestoneStatusText } from '../../../../../../helpers/milestoneHelper'
+
+import {
+ MILESTONE_STATUS
+} from '../../../../../../config/constants'
+
+import './MilestoneTypeReporting.scss'
+import { hasPermission } from '../../../../../../helpers/permissions'
+import { PERMISSIONS } from '../../../../../../config/permissions'
+
+class MilestoneTypeReporting extends React.Component {
+ constructor(props) {
+ super(props)
+
+ this.state = {
+ isEditingReport: false,
+ reportText: '',
+ }
+
+ this.onClickProvideReport = this.onClickProvideReport.bind(this)
+ this.onReportTextChange = this.onReportTextChange.bind(this)
+ this.onClickPublishReport = this.onClickPublishReport.bind(this)
+ this.onClickCancelProvideReport = this.onClickCancelProvideReport.bind(this)
+ this.onClickSaveReport = this.onClickSaveReport.bind(this)
+ }
+
+ onClickProvideReport() {
+ const { milestone } = this.props
+
+ this.setState({
+ isEditingReport: true,
+ reportText: _.get(milestone, 'details.content.report')
+ })
+ }
+
+ onClickCancelProvideReport() {
+ this.setState({
+ isEditingReport: false,
+ })
+ }
+
+ onReportTextChange(event) {
+ this.setState({
+ reportText: event.target.value,
+ })
+ }
+
+ onClickPublishReport() {
+ const { reportText } = this.state
+ const { updateMilestoneContent } = this.props
+
+ updateMilestoneContent({ report: reportText }, undefined, MILESTONE_STATUS.COMPLETED)
+ }
+
+ onClickSaveReport() {
+ const { reportText } = this.state
+ const { updateMilestoneContent } = this.props
+
+ updateMilestoneContent({ report: reportText })
+ }
+
+ render() {
+ const {
+ milestone,
+ theme,
+ } = this.props
+ const {
+ isEditingReport,
+ reportText,
+ } = this.state
+
+ const isActive = milestone.status === MILESTONE_STATUS.ACTIVE
+ const isCompleted = milestone.status === MILESTONE_STATUS.COMPLETED
+
+ const canManage = hasPermission(PERMISSIONS.MANAGE_MILESTONE)
+
+ return (
+
+
+
+
+
+ {/* Report Edit */}
+ {isEditingReport && (
+
+ )}
+
+ {/*
+ Active status
+ */}
+ {isActive && (
+
+ {canManage && (
+ !isEditingReport && (
+
+
+
+ )
+ )}
+
+ )}
+
+ {/* Completed status */}
+ {isCompleted && !isEditingReport && (
+
+
{_.get(milestone, 'details.content.report', '')}
+ {
+ canManage && (
+
+
+
+ )
+ }
+
+ )}
+
+
+ )
+ }
+}
+
+MilestoneTypeReporting.defaultProps = {
+ theme: null,
+}
+
+MilestoneTypeReporting.propTypes = {
+ milestone: PT.object,
+ theme: PT.string,
+ updateMilestoneContent: PT.func.isRequired,
+}
+
+export default withMilestoneExtensionRequest(MilestoneTypeReporting)
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.scss b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.scss
new file mode 100644
index 000000000..4a8640088
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/MilestoneTypeReporting.scss
@@ -0,0 +1,25 @@
+@import '../../../../../../styles/includes';
+@import '~tc-ui/src/styles/tc-includes';
+
+.milestone-post {
+ @include roboto;
+ font-size: 15px;
+
+ .btns-container {
+ margin-top: 8px;
+ > *:not(:first-child) {
+ margin-left: 8px;
+ }
+ }
+
+ .report-textarea {
+ height: unset;
+ margin-top: 8px;
+ }
+
+ .view-report-text {
+ margin-top: 8px;
+ white-space: pre-wrap;
+ }
+
+}
diff --git a/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/index.js b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/index.js
new file mode 100644
index 000000000..9766e2698
--- /dev/null
+++ b/src/projects/detail/components/timeline/milestones/MilestoneTypeReporting/index.js
@@ -0,0 +1,2 @@
+import MilestoneTypeReporting from './MilestoneTypeReporting'
+export default MilestoneTypeReporting
diff --git a/src/projects/detail/containers/ProductTimelineContainer.jsx b/src/projects/detail/containers/ProductTimelineContainer.jsx
index c2dba83fa..af4ddf05c 100644
--- a/src/projects/detail/containers/ProductTimelineContainer.jsx
+++ b/src/projects/detail/containers/ProductTimelineContainer.jsx
@@ -22,6 +22,7 @@ import {
completeFinalFixesMilestone,
extendProductMilestone,
submitFinalFixesRequest,
+ submitDeliverableFinalFixesRequest,
} from '../../actions/productsTimelines'
class ProductTimelineContainer extends React.Component {
@@ -68,6 +69,7 @@ const mapDispatchToProps = {
completeFinalFixesMilestone,
extendProductMilestone,
submitFinalFixesRequest,
+ submitDeliverableFinalFixesRequest,
}
export default connect(mapStateToProps, mapDispatchToProps)(ProductTimelineContainer)