Skip to content

Commit 1d75c13

Browse files
author
vikasrohit
authored
Merge pull request #1261 from appirio-tech/feature/sync-url-change-for-logged-in-user
Feature/sync url change for logged in user
2 parents c71006c + 8719188 commit 1d75c13

File tree

5 files changed

+55
-93
lines changed

5 files changed

+55
-93
lines changed

src/components/TopBar/ProjectsToolBar.js

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,18 @@
11
require('./ProjectsToolBar.scss')
22

33
import React, {PropTypes, Component} from 'react'
4+
import { Link } from 'react-router'
45
import { connect } from 'react-redux'
56
import cn from 'classnames'
67
import _ from 'lodash'
7-
import Modal from 'react-modal'
88
import { SearchBar } from 'appirio-tech-react-components'
99
import Filters from './Filters'
1010

11-
import ModalControl from '../ModalControl'
12-
import SVGIconImage from '../SVGIconImage'
13-
import CoderBot from '../CoderBot/CoderBot'
14-
import ProjectWizard from '../../projects/create/components/ProjectWizard'
15-
16-
import { createProjectWithStatus as createProjectAction } from '../../projects/actions/project'
1711
import { projectSuggestions, loadProjects } from '../../projects/actions/loadProjects'
1812
import {
1913
ROLE_CONNECT_COPILOT,
2014
ROLE_CONNECT_MANAGER,
21-
ROLE_ADMINISTRATOR,
22-
PROJECT_STATUS_IN_REVIEW
15+
ROLE_ADMINISTRATOR
2316
} from '../../config/constants'
2417

2518

@@ -28,18 +21,14 @@ class ProjectsToolBar extends Component {
2821
constructor(props) {
2922
super(props)
3023
this.state = {
31-
isCreateProjectModalVisible : false,
3224
errorCreatingProject: false,
3325
isFilterVisible: false
3426
}
3527
this.applyFilters = this.applyFilters.bind(this)
3628
this.toggleFilter = this.toggleFilter.bind(this)
37-
this.showCreateProjectDialog = this.showCreateProjectDialog.bind(this)
38-
this.hideCreateProjectDialog = this.hideCreateProjectDialog.bind(this)
3929
this.handleTermChange = this.handleTermChange.bind(this)
4030
this.handleSearch = this.handleSearch.bind(this)
4131
this.handleMyProjectsFilter = this.handleMyProjectsFilter.bind(this)
42-
this.createProject = this.createProject.bind(this)
4332
this.onLeave = this.onLeave.bind(this)
4433
}
4534

@@ -51,7 +40,6 @@ class ProjectsToolBar extends Component {
5140
this.setState({
5241
isProjectDirty : false
5342
}, () => {
54-
this.hideCreateProjectDialog()
5543
this.props.router.push('/projects/' + nextProps.project.id)
5644
})
5745
} else {
@@ -103,26 +91,6 @@ class ProjectsToolBar extends Component {
10391
this.applyFilters({memberOnly: event.target.checked})
10492
}
10593

106-
showCreateProjectDialog() {
107-
this.setState({
108-
isCreateProjectModalVisible : true
109-
})
110-
}
111-
112-
hideCreateProjectDialog() {
113-
let confirm = true
114-
if (this.state.isProjectDirty) {
115-
confirm = window.confirm('You have unsaved changes. Are you sure you want to leave?')
116-
}
117-
if (confirm === true) {
118-
this.setState({
119-
isProjectDirty: false,
120-
isCreateProjectModalVisible : false,
121-
errorCreatingProject: false
122-
})
123-
}
124-
}
125-
12694
applyFilters(filter) {
12795
const criteria = _.assign({}, this.props.criteria, filter)
12896
if (criteria && criteria.keyword) {
@@ -155,62 +123,27 @@ class ProjectsToolBar extends Component {
155123
this.props.loadProjects(criteria, page)
156124
}
157125

158-
createProject(project) {
159-
this.props.createProjectAction(project, PROJECT_STATUS_IN_REVIEW)
160-
}
161-
162126
shouldComponentUpdate(nextProps, nextState) {
163127
const { user, criteria, creatingProject, projectCreationError, searchTermTag } = this.props
164-
const { isCreateProjectModalVisible, errorCreatingProject, isFilterVisible } = this.state
128+
const { errorCreatingProject, isFilterVisible } = this.state
165129
return nextProps.user.handle !== user.handle
166130
|| JSON.stringify(nextProps.criteria) !== JSON.stringify(criteria)
167131
|| nextProps.creatingProject !== creatingProject
168132
|| nextProps.projectCreationError !== projectCreationError
169133
|| nextProps.searchTermTag !== searchTermTag
170-
|| nextState.isCreateProjectModalVisible !== isCreateProjectModalVisible
171134
|| nextState.errorCreatingProject !== errorCreatingProject
172135
|| nextState.isFilterVisible !== isFilterVisible
173136
}
174137

175138
render() {
176139
const { logo, userMenu, userRoles, criteria, isPowerUser } = this.props
177-
const { isCreateProjectModalVisible, errorCreatingProject, isFilterVisible } = this.state
140+
const { isFilterVisible } = this.state
178141
const isLoggedIn = userRoles && userRoles.length
179142

180143
const noOfFilters = _.keys(criteria).length - 1 // -1 for default sort criteria
181144

182145
return (
183146
<div className="ProjectsToolBar">
184-
<Modal
185-
isOpen={ isCreateProjectModalVisible }
186-
className="project-creation-dialog"
187-
overlayClassName="project-creation-dialog-overlay"
188-
onRequestClose={ this.hideCreateProjectDialog }
189-
contentLabel=""
190-
>
191-
<ModalControl
192-
className="escape-button"
193-
icon={<SVGIconImage filePath="x-mark" />}
194-
label="esc"
195-
onClick={ this.hideCreateProjectDialog }
196-
/>
197-
{ !errorCreatingProject &&
198-
<ProjectWizard
199-
showModal={ false }
200-
processing={ this.props.creatingProject }
201-
createProject={ this.createProject }
202-
closeModal={ this.hideCreateProjectDialog }
203-
userRoles={ userRoles }
204-
onProjectUpdate={ (updatedProject, dirty=true) => {
205-
this.setState({
206-
isProjectDirty: dirty
207-
})
208-
}
209-
}
210-
/>
211-
}
212-
{ errorCreatingProject && <CoderBot code={ 500 } message="Unable to create project" />}
213-
</Modal>
214147
<div className="primary-toolbar">
215148
{ logo }
216149
{
@@ -240,7 +173,7 @@ class ProjectsToolBar extends Component {
240173
{
241174
!!isLoggedIn &&
242175
<div>
243-
<a onClick={ this.showCreateProjectDialog } href="javascript:" className="tc-btn tc-btn-sm tc-btn-primary">+ New Project</a>
176+
<Link to="/new-project" className="tc-btn tc-btn-sm tc-btn-primary">+ New Project</Link>
244177
</div>
245178
}
246179
{ userMenu }
@@ -289,6 +222,6 @@ const mapStateToProps = ({ projectSearchSuggestions, searchTerm, projectSearch,
289222
}
290223
}
291224

292-
const actionsToBind = { projectSuggestions, loadProjects, createProjectAction }
225+
const actionsToBind = { projectSuggestions, loadProjects }
293226

294227
export default connect(mapStateToProps, actionsToBind)(ProjectsToolBar)

src/config/projectWizard/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const products = {
116116
details: 'Translate designs to a web (HTML/CSS/JavaScript) or mobile prototype',
117117
icon: 'product-dev-prototype',
118118
id: 'visual_prototype',
119-
aliases: ['visual-prototype','visual_prototype'],
119+
aliases: ['visual-prototype', 'visual_prototype'],
120120
disabled: true
121121
},
122122
'Front-end': {

src/projects/create/components/ProjectWizard.jsx

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ProjectWizard extends Component {
5151
const incompleteProject = JSON.parse(incompleteProjectStr)
5252
const incompleteProduct = _.get(incompleteProject, 'details.products[0]')
5353
let wizardStep = WZ_STEP_INCOMP_PROJ_CONF
54+
let updateQuery = {}
5455
if (incompleteProduct && params && params.product) {
5556
// assumes the params.product to be id of a product because incomplete project is set only
5657
// after user selects a particular product
@@ -59,20 +60,21 @@ class ProjectWizard extends Component {
5960
// load project detais page directly if product of save project and deep link are the same
6061
if (product.id === incompleteProduct) {
6162
wizardStep = WZ_STEP_FILL_PROJ_DETAILS
62-
} else { // for different product type, update local state with URL params
63-
const projectType = findProductCategory(params.product)
64-
_.set(incompleteProject, 'type', projectType.id)
65-
_.set(incompleteProject, 'details.products[0]', product.id)
63+
updateQuery = {$merge : incompleteProject}
64+
} else {
65+
// explicitly ignores the wizardStep returned by the method
66+
// we need to call this method just to get updateQuery updated with correct project type and product
67+
this.loadProjectAndProductFromURL(params, updateQuery)
6668
}
6769
}
6870
}
6971
this.setState({
70-
project: update(this.state.project, {$merge : incompleteProject}),
71-
dirtyProject: update(this.state.dirtyProject, {$merge : incompleteProject}),
72-
wizardStep: wizardStep,
72+
project: update(this.state.project, updateQuery),
73+
dirtyProject: update(this.state.dirtyProject, updateQuery),
74+
wizardStep,
7375
isProjectDirty: false
7476
}, () => {
75-
typeof onStepChange === 'function' && onStepChange(this.state.wizardStep)
77+
typeof onStepChange === 'function' && onStepChange(this.state.wizardStep, this.state.project)
7678
})
7779
} else {
7880
// if there is no incomplete project in the local storage, load the wizard with appropriate step
@@ -111,7 +113,7 @@ class ProjectWizard extends Component {
111113
let wizardStep = type && product ? WZ_STEP_FILL_PROJ_DETAILS : null
112114
const updateQuery = {}
113115
if (params && params.product) { // if there exists product path param
114-
wizardStep = this.loadProjectAndProductFromURL(params.product, updateQuery)
116+
wizardStep = this.loadProjectAndProductFromURL(params, updateQuery)
115117
} else { // if there is not product path param, it should be first step of the wizard
116118
updateQuery['type'] = { $set : null }
117119
updateQuery['details'] = { products : { $set: [] } }
@@ -132,22 +134,28 @@ class ProjectWizard extends Component {
132134
/**
133135
* Loads project type and product from the given URL parameter.
134136
*
135-
* @param {string} urlParam URL parameter value which represents either project type or product
137+
* @param {object} urlParams URL parameters map
136138
* @param {object} updateQuery query object which would be updated according to parsed project type and product
137139
*
138140
* @return {number} step where wizard should move after parsing the URL param
139141
*/
140-
loadProjectAndProductFromURL(urlParam, updateQuery) {
142+
loadProjectAndProductFromURL(urlParams, updateQuery) {
143+
const productParam = urlParams && urlParams.product
144+
const statusParam = urlParams && urlParams.status
145+
if ('incomplete' === statusParam) {
146+
return WZ_STEP_INCOMP_PROJ_CONF
147+
}
148+
if (!productParam) return
141149
// first try the path param to be a project category
142-
let projectType = findCategory(urlParam, true)
150+
let projectType = findCategory(productParam, true)
143151
if (projectType) {// if its a category
144152
updateQuery['type'] = { $set : projectType.id }
145153
return WZ_STEP_SELECT_PROD_TYPE
146154
} else {
147155
// if it is not a category, it should be a product and we should be able to find a category for it
148-
projectType = findProductCategory(urlParam, true)
156+
projectType = findProductCategory(productParam, true)
149157
// finds product object from product alias
150-
const product = findProduct(urlParam, true)
158+
const product = findProduct(productParam, true)
151159
if (projectType) {// we can have `incomplete` as params.product
152160
updateQuery['type'] = { $set : projectType.id }
153161
updateQuery['details'] = { products : { $set: [product.id] } }
@@ -180,21 +188,25 @@ class ProjectWizard extends Component {
180188
* Removed incomplete project from the local storage and resets the state. Also, moves wizard to the first step.
181189
*/
182190
removeIncompleteProject() {
183-
const { onStepChange, onProjectUpdate } = this.props
191+
const { onStepChange } = this.props
184192
// remove incomplete project from local storage
185193
window.localStorage.removeItem(LS_INCOMPLETE_PROJECT)
186194
const projectType = _.get(this.state.project, 'type')
187195
const product = _.get(this.state.project, 'details.products[0]')
188196
let wizardStep = WZ_STEP_SELECT_PROJ_TYPE
197+
let project = null
189198
if (product) {
199+
project = { type: projectType, details: { products: [product] } }
190200
wizardStep = WZ_STEP_FILL_PROJ_DETAILS
191201
} else if (projectType) {
202+
project = { type: projectType, details: { products: [] } }
192203
wizardStep = WZ_STEP_SELECT_PROD_TYPE
193204
}
194205
this.setState({
195-
wizardStep: wizardStep
206+
project: _.merge({}, project),
207+
dirtyProject: _.merge({}, project),
208+
wizardStep
196209
}, () => {
197-
typeof onProjectUpdate === 'function' && onProjectUpdate(this.state.dirtyProject, true)
198210
typeof onStepChange === 'function' && onStepChange(this.state.wizardStep, this.state.project)
199211
})
200212
}

src/projects/create/containers/CreateContainer.jsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ class CreateConainer extends React.Component {
5858
}
5959
this.createProject = this.createProject.bind(this)
6060
this.onLeave = this.onLeave.bind(this)
61+
this.closeWizard = this.closeWizard.bind(this)
6162
}
6263

6364
componentWillReceiveProps(nextProps) {
@@ -139,12 +140,26 @@ class CreateConainer extends React.Component {
139140
})
140141
}
141142

143+
closeWizard() {
144+
const { userRoles } = this.props
145+
const isLoggedIn = userRoles && userRoles.length > 0
146+
// calls leave handler
147+
this.onLeave()
148+
if (isLoggedIn) {
149+
this.props.router.push('/projects')
150+
} else {
151+
this.props.router.push('/')
152+
}
153+
}
154+
142155
render() {
143156
return (
144157
<EnhancedCreateView
145158
{...this.props}
146159
createProject={ this.createProject }
147160
processing={ this.state.creatingProject }
161+
showModal
162+
closeModal={ this.closeWizard }
148163
onStepChange={ (wizardStep, updatedProject) => {
149164
// type of the project
150165
let projectType = _.get(updatedProject, 'type', null)
@@ -159,7 +174,9 @@ class CreateConainer extends React.Component {
159174
// updates the productType variable to use first alias to create SEO friendly URL
160175
productType = _.get(product, 'aliases[0]', productType)
161176
if (wizardStep === ProjectWizard.Steps.WZ_STEP_INCOMP_PROJ_CONF) {
162-
browserHistory.push(NEW_PROJECT_PATH + '/incomplete')
177+
let productUrl = productType ? ('/' + productType) : ''
178+
productUrl = !productType && projectType ? ('/' + projectType) : productUrl
179+
browserHistory.push(NEW_PROJECT_PATH + productUrl + '/incomplete')
163180
}
164181
if (wizardStep === ProjectWizard.Steps.WZ_STEP_SELECT_PROJ_TYPE) {
165182
browserHistory.push(NEW_PROJECT_PATH + '/' + window.location.search)

src/routes.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ const renderTopBarWithProjectsToolBar = (props) => <TopBarContainer toolbar={ Pr
104104
export default (
105105
<Route path="/" onUpdate={() => window.scrollTo(0, 0)} component={ App } onEnter={ redirectToConnect }>
106106
<IndexRoute components={{ topbar: renderTopBarWithProjectsToolBar, content: Home }} />
107-
<Route path="/new-project(/:product)" components={{ topbar: null, content: CreateContainer }} onEnter={ validateCreateProjectParams } />
107+
<Route path="/new-project(/:product)(/:status)" components={{ topbar: null, content: CreateContainer }} onEnter={ validateCreateProjectParams } />
108108
<Route path="/new-project-callback" components={{ topbar: null, content: CreateContainer }} />
109109
<Route path="/terms" components={{ topbar: renderTopBarWithProjectsToolBar, content: ConnectTerms }} />
110110
<Route path="/login" components={{ topbar: renderTopBarWithProjectsToolBar, content: LoginRedirect }} />

0 commit comments

Comments
 (0)