Skip to content

Commit 95236fa

Browse files
author
Vikas Agarwal
committed
Merge branch 'dev' into feature/features_change_no_reflected_without_save
* dev: Github issue#873, Manager does not get manager access immediately after joining the project -- Updated projectNonDirty state as well on every action that can update project Github issue#802, can add more screen -- Utilized the on the fly update of other form sections on every form change event. Updated SpecQuestion component to use dirty project value for numberOfScreens question
2 parents e5f4251 + c84b030 commit 95236fa

File tree

4 files changed

+78
-21
lines changed

4 files changed

+78
-21
lines changed

src/projects/detail/components/EditProjectForm.jsx

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { Component, PropTypes } from 'react'
22
import { withRouter } from 'react-router'
33
import Modal from 'react-modal'
44
import _ from 'lodash'
5+
import { unflatten } from 'flat'
56
import update from 'react-addons-update'
67
import FeaturePicker from './FeatureSelector/FeaturePicker'
78
import { Formsy, Icons } from 'appirio-tech-react-components'
@@ -64,8 +65,15 @@ class EditProjectForm extends Component {
6465
}
6566

6667
componentWillReceiveProps(nextProps) {
67-
// we receipt property updates from PROJECT_DIRTY REDUX state
68-
if (nextProps.project.isDirty) return
68+
// we received property updates from PROJECT_DIRTY REDUX state
69+
if (nextProps.project.isDirty) {
70+
this.setState({
71+
// sets a new state variable with dirty project
72+
// any component who wants to listen for unsaved changes in project form can listen to this state variable
73+
dirtyProject : Object.assign({}, nextProps.project)
74+
})
75+
return
76+
}
6977
let updatedProject = Object.assign({}, nextProps.project)
7078
if (this.state.isFeaturesDirty && !this.state.isSaving) {
7179
updatedProject = update(updatedProject, {
@@ -157,7 +165,6 @@ class EditProjectForm extends Component {
157165
}
158166

159167
submit(model) {
160-
console.log('submit', this.isChanged())
161168
// if (this.state.isFeaturesDirty) {
162169
// model.details.appDefinition.features = this.state.project.details.appDefinition.features
163170
// }
@@ -173,20 +180,21 @@ class EditProjectForm extends Component {
173180
*/
174181
handleChange(change) {
175182
// removed check for isChanged argument to fire the PROJECT_DIRTY event for every change in the form
176-
this.props.fireProjectDirty(change)
183+
this.props.fireProjectDirty(unflatten(change))
177184
}
178185

179186

180187
render() {
181188
const { isEdittable, sections } = this.props
182-
const { project } = this.state
189+
const { project, dirtyProject } = this.state
183190
const renderSection = (section, idx) => {
184191
const anySectionInvalid = _.some(this.props.sections, (s) => s.isInvalid)
185192
return (
186193
<div key={idx}>
187194
<SpecSection
188195
{...section}
189196
project={project}
197+
dirtyProject={dirtyProject}
190198
sectionNumber={idx + 1}
191199
resetFeatures={this.onFeaturesSaveAttachedClick}
192200
showFeaturesDialog={this.showFeaturesDialog}

src/projects/detail/components/SpecQuestions.jsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const getIcon = icon => {
2626
}
2727
}
2828

29-
const SpecQuestions = ({questions, project, resetFeatures, showFeaturesDialog, isRequired}) => {
29+
const SpecQuestions = ({questions, project, dirtyProject, resetFeatures, showFeaturesDialog, isRequired}) => {
3030

3131
const renderQ = (q, index) => {
3232
// let child = null
@@ -36,9 +36,9 @@ const SpecQuestions = ({questions, project, resetFeatures, showFeaturesDialog, i
3636
label: q.label,
3737
value: _.get(project, q.fieldName, '')
3838
}
39-
4039
if (q.fieldName === 'details.appDefinition.numberScreens') {
41-
const screens = _.get(project, 'details.appScreens.screens', [])
40+
const p = dirtyProject ? dirtyProject : project
41+
const screens = _.get(p, 'details.appScreens.screens', [])
4242
const definedScreens = screens.length
4343
_.each(q.options, (option) => {
4444
let maxValue = 0
@@ -145,9 +145,26 @@ const SpecQuestions = ({questions, project, resetFeatures, showFeaturesDialog, i
145145
}
146146

147147
SpecQuestions.propTypes = {
148+
/**
149+
* Original project object for which questions are to be rendered
150+
*/
148151
project: PropTypes.object.isRequired,
152+
/**
153+
* Dirty project with all unsaved changes
154+
*/
155+
dirtyProject: PropTypes.object,
156+
/**
157+
* Callback to be called when user clicks on Add/Edit Features button in feature picker component
158+
*/
149159
showFeaturesDialog: PropTypes.func.isRequired,
160+
/**
161+
* Call back to be called when user resets features from feature picker.
162+
* NOTE: It seems it is not used as of now by feature picker component
163+
*/
150164
resetFeatures: PropTypes.func.isRequired,
165+
/**
166+
* Array of questions to be rendered. This comes from the spec template for the product
167+
*/
151168
questions: PropTypes.arrayOf(PropTypes.object).isRequired
152169
}
153170

src/projects/detail/components/SpecSection.jsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { PROJECT_STATUS_DRAFT, PROJECT_NAME_MAX_LENGTH, PROJECT_REF_CODE_MAX_LEN
1010
const SpecSection = props => {
1111
const {
1212
project,
13+
dirtyProject,
1314
resetFeatures,
1415
showFeaturesDialog,
1516
id,
@@ -61,6 +62,7 @@ const SpecSection = props => {
6162
resetFeatures={resetFeatures}
6263
questions={props.questions}
6364
project={project}
65+
dirtyProject={dirtyProject}
6466
isRequired={props.required}
6567
/>
6668
)

src/projects/reducers/project.js

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { unflatten } from 'flat'
21
import {
32
LOAD_PROJECT_PENDING, LOAD_PROJECT_SUCCESS, LOAD_PROJECT_FAILURE, LOAD_DIRECT_PROJECT_SUCCESS,
43
CREATE_PROJECT_PENDING, CREATE_PROJECT_SUCCESS, CREATE_PROJECT_FAILURE, CLEAR_LOADED_PROJECT,
@@ -21,9 +20,12 @@ const initialState = {
2120
processingMembers: false,
2221
processingAttachments: false,
2322
error: false,
24-
project: {}
23+
project: {},
24+
projectNonDirty: {}
2525
}
2626

27+
// NOTE: We should always update projectNonDirty state whenever we update the project state
28+
2729
const parseErrorObj = (action) => {
2830
const data = action.payload.response.data.result
2931
return {
@@ -40,7 +42,8 @@ export const projectState = function (state=initialState, action) {
4042
case LOAD_PROJECT_PENDING:
4143
return Object.assign({}, state, {
4244
isLoading: true,
43-
project: null
45+
project: null,
46+
projectNonDirty: null
4447
})
4548

4649
case LOAD_PROJECT_SUCCESS:
@@ -54,7 +57,8 @@ export const projectState = function (state=initialState, action) {
5457
case CLEAR_LOADED_PROJECT:
5558
case GET_PROJECTS_SUCCESS:
5659
return Object.assign({}, state, {
57-
project: {}
60+
project: {},
61+
projectNonDirty: {}
5862
})
5963

6064
case LOAD_DIRECT_PROJECT_SUCCESS:
@@ -70,6 +74,18 @@ export const projectState = function (state=initialState, action) {
7074
plannedDuration: action.payload.plannedDuration,
7175
projectedDuration: action.payload.projectedDuration
7276
}}
77+
},
78+
projectNonDirty: {
79+
budget: { $set: {
80+
actualCost: action.payload.actualCost,
81+
projectedCost: action.payload.projectedCost,
82+
totalBudget: action.payload.totalBudget
83+
}},
84+
duration: { $set: {
85+
actualDuration: action.payload.actualDuration,
86+
plannedDuration: action.payload.plannedDuration,
87+
projectedDuration: action.payload.projectedDuration
88+
}}
7389
}
7490
})
7591

@@ -96,7 +112,8 @@ export const projectState = function (state=initialState, action) {
96112
return Object.assign({}, state, {
97113
processing: false,
98114
error: false,
99-
project: {}
115+
project: {},
116+
projectNonDirty: {}
100117
})
101118

102119
// Project attachments
@@ -110,15 +127,17 @@ export const projectState = function (state=initialState, action) {
110127
case ADD_PROJECT_ATTACHMENT_SUCCESS:
111128
return update(state, {
112129
processingAttachments: { $set : false },
113-
project: { attachments: { $push: [action.payload] } }
130+
project: { attachments: { $push: [action.payload] } },
131+
projectNonDirty: { attachments: { $push: [action.payload] } }
114132
})
115133

116134
case UPDATE_PROJECT_ATTACHMENT_SUCCESS: {
117135
// get index
118136
const idx = _.findIndex(state.project.attachments, a => a.id === action.payload.id)
119137
return update(state, {
120138
processingAttachments: { $set : false },
121-
project: { attachments: { $splice : [[idx, 1, action.payload]] } }
139+
project: { attachments: { $splice : [[idx, 1, action.payload]] } },
140+
projectNonDirty: { attachments: { $splice : [[idx, 1, action.payload]] } }
122141
})
123142
}
124143

@@ -128,7 +147,8 @@ export const projectState = function (state=initialState, action) {
128147
const idx = _.findIndex(state.project.attachments, a => a.id === action.payload)
129148
return update(state, {
130149
processing: { $set : false },
131-
project: { attachments: { $splice: [[idx, 1]] } }
150+
project: { attachments: { $splice: [[idx, 1]] } },
151+
projectNonDirty: { attachments: { $splice: [[idx, 1]] } }
132152
})
133153
}
134154

@@ -142,7 +162,8 @@ export const projectState = function (state=initialState, action) {
142162
case ADD_PROJECT_MEMBER_SUCCESS:
143163
return update (state, {
144164
processingMembers: { $set : false },
145-
project: { members: { $push: [action.payload] } }
165+
project: { members: { $push: [action.payload] } },
166+
projectNonDirty: { members: { $push: [action.payload] } }
146167
})
147168

148169
case UPDATE_PROJECT_MEMBER_SUCCESS: {
@@ -156,7 +177,8 @@ export const projectState = function (state=initialState, action) {
156177
updatedMembers.splice(idx, 1, action.payload)
157178
return update(state, {
158179
processingMembers: { $set : false },
159-
project: { members: { $set: updatedMembers } }
180+
project: { members: { $set: updatedMembers } },
181+
projectNonDirty: { members: { $set: updatedMembers } }
160182
})
161183
}
162184

@@ -165,13 +187,21 @@ export const projectState = function (state=initialState, action) {
165187
const idx = _.findIndex(state.project.members, a => a.id === action.payload)
166188
return update(state, {
167189
processingMembers: { $set : false },
168-
project: { members: { $splice: [[idx, 1]] } }
190+
project: { members: { $splice: [[idx, 1]] } },
191+
projectNonDirty: { members: { $splice: [[idx, 1]] } }
169192
})
170193
}
171194

172-
case PROJECT_DIRTY: {
195+
case PROJECT_DIRTY: {// payload contains only changed values from the project form
173196
return Object.assign({}, state, {
174-
project: _.merge({}, state.project, unflatten(action.payload), { isDirty : true})
197+
project: _.mergeWith({}, state.project, action.payload, { isDirty : true},
198+
// customizer to override screens array with changed values
199+
(objValue, srcValue, key) => {
200+
if (key === 'screens' || key === 'features') {
201+
return srcValue// srcValue contains the changed values from action payload
202+
}
203+
}
204+
)
175205
})
176206
}
177207

0 commit comments

Comments
 (0)