diff --git a/src/config/projectQuestions/app_dev.v1.0.js b/src/config/projectQuestions/app_dev.v1.0.js index 2bbca66d2..35bc051ac 100644 --- a/src/config/projectQuestions/app_dev.v1.0.js +++ b/src/config/projectQuestions/app_dev.v1.0.js @@ -20,7 +20,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/avd.v1.0.js b/src/config/projectQuestions/avd.v1.0.js index 1200b92b0..335e702b7 100644 --- a/src/config/projectQuestions/avd.v1.0.js +++ b/src/config/projectQuestions/avd.v1.0.js @@ -22,7 +22,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/generic_chatbot.v1.0.js b/src/config/projectQuestions/generic_chatbot.v1.0.js index f3758c37b..92259488c 100644 --- a/src/config/projectQuestions/generic_chatbot.v1.0.js +++ b/src/config/projectQuestions/generic_chatbot.v1.0.js @@ -20,7 +20,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/ibm_chatbot.v1.0.js b/src/config/projectQuestions/ibm_chatbot.v1.0.js index bfaf6f73c..b0269210e 100644 --- a/src/config/projectQuestions/ibm_chatbot.v1.0.js +++ b/src/config/projectQuestions/ibm_chatbot.v1.0.js @@ -20,7 +20,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/real_world_testing.v1.0.js b/src/config/projectQuestions/real_world_testing.v1.0.js index 51eb32c4a..dfdd6a3db 100644 --- a/src/config/projectQuestions/real_world_testing.v1.0.js +++ b/src/config/projectQuestions/real_world_testing.v1.0.js @@ -23,7 +23,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/visual_design.v1.0.js b/src/config/projectQuestions/visual_design.v1.0.js index bd8e53c97..86114a5d5 100644 --- a/src/config/projectQuestions/visual_design.v1.0.js +++ b/src/config/projectQuestions/visual_design.v1.0.js @@ -22,7 +22,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectQuestions/wireframes.v1.0.js b/src/config/projectQuestions/wireframes.v1.0.js index e85420950..cdf319688 100644 --- a/src/config/projectQuestions/wireframes.v1.0.js +++ b/src/config/projectQuestions/wireframes.v1.0.js @@ -22,7 +22,7 @@ const sections = [ const product = _.get(project, 'details.products[0]') if (showProduct && product) { const prd = findProduct(product) - if (prd) return prd + if (prd) return prd.name } return 'Definition' }, diff --git a/src/config/projectSpecification/typeToSpecification.json b/src/config/projectSpecification/typeToSpecification.json index 6e9cfe7b6..70f10e0bc 100644 --- a/src/config/projectSpecification/typeToSpecification.json +++ b/src/config/projectSpecification/typeToSpecification.json @@ -8,7 +8,7 @@ "website_development": "app_dev.v1.0", "application_development": "app_dev.v1.0", "watson_chatbot": "ibm_chatbot.v1.0", - "chatbot": "generic_chatbot.v1.0", + "generic_chatbot": "generic_chatbot.v1.0", "generic_dev": "app_dev.v1.0", "real_world_testing": "real_world_testing.v1.0", "mobility_testing": "app_dev.v1.0", diff --git a/src/config/projectWizard/index.js b/src/config/projectWizard/index.js index 54f6af940..c2f99f994 100644 --- a/src/config/projectWizard/index.js +++ b/src/config/projectWizard/index.js @@ -7,12 +7,14 @@ const products = { info: 'Build a phone, tablet, wearable, or desktop app', question: 'What do you need to develop?', id: 'app', + aliases: ['all-apps'], subtypes: { App: { brief: 'Apps', details: 'Build apps for mobile, web, or wearables', icon: 'product-app-app', - id: 'application_development' + id: 'application_development', + aliases: ['app', 'application_development'] } } }, @@ -21,12 +23,14 @@ const products = { info: 'Design and build the high-impact pages for your blog, online store, or company', question: 'What do you need to develop?', id: 'website', + aliases: ['all-websites'], subtypes: { Website: { brief: 'Websites', details: 'Build responsive or regular websites', icon: 'product-website-website', - id: 'website_development' + id: 'website_development', + aliases: ['website', 'website_development'] } } }, @@ -34,20 +38,23 @@ const products = { icon: 'product-cat-chatbot', info: 'Build, train and test a custom conversation for your chatbot', question: 'What do you need to develop?', - id: 'all-chatbots', + id: 'chatbot', + aliases: ['all-chatbots'], subtypes: { 'Watson Chatbot': { brief: 'Watson Chatbot', details: 'Build Chatbot using IBM Watson', icon: 'product-chatbot-watson', id: 'watson_chatbot', + aliases: ['watson_chatbot'], hidden: true }, Chatbot: { brief: 'Chatbot', details: 'Build, train and test a custom conversation for your chat bot', icon: 'product-chatbot-chatbot', - id: 'chatbot' + id: 'generic_chatbot', + aliases: ['chatbot', 'generic_chatbot'] } } }, @@ -56,38 +63,44 @@ const products = { info: 'Pick the right design project for your needs - wireframes, visual, or other', question: 'What kind of design do you need?', id: 'visual_design', + aliases: ['all-designs'], subtypes: { Wireframes: { brief: '10-15 screens', details: 'Plan and explore the navigation and structure of your app', icon: 'product-design-wireframes', - id: 'wireframes' + id: 'wireframes', + aliases: ['wireframes'] }, 'App Visual Design - Concepts': { brief: '1-15 screens', details: 'Visualize and test your app requirements and ideas', icon: 'product-design-app-visual', id: 'visual_design_concepts', + aliases: ['visual_design_concepts'], disabled: true }, 'Visual Design': { brief: '1-15 screens', details: 'Create development-ready designs', icon: 'product-design-app-visual', - id: 'visual_design_prod' + id: 'visual_design_prod', + aliases: ['visual-design', 'visual_design_prod'] }, Infographic: { brief: 'Infographic', details: 'Present your data in an easy-to-understand and interesting way', icon: 'product-design-infographic', id: 'infographic', + aliases: ['infographic'], disabled: true }, 'Other Design': { brief: 'other designs', details: 'Get help with other types of design', icon: 'product-design-other', - id: 'generic_design' + id: 'generic_design', + aliases: ['generic-design', 'generic_design'] } } }, @@ -96,31 +109,36 @@ const products = { info: 'Get help with any part of your development lifecycle', question: 'What kind of development do you need?', id: 'app_dev', + aliases: ['all-development'], subtypes: { 'Front-end Prototype': { brief: '3-20 screens', details: 'Translate designs to a web (HTML/CSS/JavaScript) or mobile prototype', icon: 'product-dev-prototype', id: 'visual_prototype', + aliases: ['visual-prototype','visual_prototype'], disabled: true }, 'Front-end': { brief: '', details: 'Translate your designs into Web or Mobile front-end', icon: 'product-dev-front-end-dev', - id: 'frontend_dev' + id: 'frontend_dev', + aliases: ['frontend-development', 'frontend_dev'] }, 'Back-end & API': { brief: '', details: 'Build the server, DB, and API for your app', icon: 'product-dev-integration', - id: 'api_dev' + id: 'api_dev', + aliases: ['api-development', 'api_dev'] }, 'Development Integration': { brief: 'Tasks or adhoc', details: 'Get help with any part of your app or software', icon: 'product-dev-other', - id: 'generic_dev' + id: 'generic_dev', + aliases: ['generic-development', 'generic_dev'] } } }, @@ -129,18 +147,21 @@ const products = { info: 'Exploratory Testing, Cross browser-device Testing', question: 'What kind of quality assurance (QA) do you need?', id: 'quality_assurance', + aliases: ['all-quality-assurance'], subtypes: { 'Real World Testing': { brief: 'TBD', details: 'Exploratory Testing, Cross browser-device Testing', icon: 'product-qa-crowd-testing', - id: 'real_world_testing' + id: 'real_world_testing', + aliases: ['real-world-testing', 'real_world_testing'] }, 'Mobility Testing': { brief: 'TBD', details: 'App Certification, Lab on Hire, User Sentiment Analysis', icon: 'product-qa-mobility-testing', id: 'mobility_testing', + aliases: ['mobility-testing', 'mobility_testing'], disabled: true }, 'Website Performance': { @@ -148,6 +169,7 @@ const products = { details: 'Webpage rendering effiency, Load, Stress and Endurance Test', icon: 'product-qa-website-performance', id: 'website_performance', + aliases: ['website-performance', 'website_performance'], disabled: true }, 'Digital Accessability': { @@ -155,6 +177,7 @@ const products = { details: 'Make sure you app or website conforms to all rules and regulations', icon: 'product-qa-digital-accessability', id: 'digital_accessability', + aliases: ['digital-accessability', 'digital_accessability'], disabled: true }, 'Open Source Automation': { @@ -162,6 +185,7 @@ const products = { details: 'Exploratory testing, cross browser testing', icon: 'product-qa-os-automation', id: 'open_source_automation', + aliases: ['open-source-automation', 'open_source_automation'], disabled: true }, 'Consulting & Adivisory': { @@ -169,6 +193,7 @@ const products = { details: 'Expert services to get your project covered end-to-end', icon: 'product-qa-consulting', id: 'consulting_adivisory', + aliases: ['consulting-adivisory', 'consulting_adivisory'], disabled: true } } @@ -180,34 +205,54 @@ export default products // exports all project types as an array export const projectTypes = _.mapValues(products, (p, key) => ({...p, name: key }) ) -export function findProduct(product) { - if (product === 'generic_dev') { - return 'Development' - } - if (product === 'generic_design') { - return 'Design' - } +/** + * Finds product for the given product id. It compares the given value against product id and aliases. + * + * @param {string} productId id of the product. + * @param {boolean} aliasesOnly flag to limit the search to aliases only + * + * @return {object} product object from the catalouge + */ +export function findProduct(productId, aliasesOnly = false) { for(const pType in products) { for(const prd in products[pType].subtypes) { - if (products[pType].subtypes[prd].id === product) { - return prd + const subType = products[pType].subtypes[prd] + if ((subType.id === productId && !aliasesOnly) || (subType.aliases && subType.aliases.indexOf(productId) !== -1)) { + return { ...subType, name: prd} } } } } -export function findCategory(categoryId) { +/** + * Finds project category for the given category id. It compares the given value against id and aliases. + * + * @param {string} categoryId id of the category. + * @param {boolean} aliasesOnly flag to limit the search to aliases only + * + * @return {object} project category object from the catalouge + */ +export function findCategory(categoryId, aliasesOnly = false) { for(const key in products) { - if (products[key].id === categoryId && !products[key].disabled) { - return { ...products[key], name: key} + const product = products[key] + if (!product.disabled && ((product.id === categoryId && !aliasesOnly) || (product.aliases && product.aliases.indexOf(categoryId) !== -1))) { + return { ...product, name: key} } } return null } -export function findProductsOfCategory(category, fetchHidden = true) { +/** + * Finds products of the given category id. Never returns disabled products + * + * @param {string} categoryId id of the category. + * @param {boolean} fetchHidden flag to limit the hidden products + * + * @return {Array} non disabled products of the given category from the catalouge + */ +export function findProductsOfCategory(categoryId, fetchHidden = true) { for(const pType in products) { - if (products[pType].id === category) { + if (products[pType].id === categoryId) { const ret = [] for(const prd in products[pType].subtypes) { const product = products[pType].subtypes[prd] @@ -220,11 +265,19 @@ export function findProductsOfCategory(category, fetchHidden = true) { } } -export function findProductCategory(product) { +/** + * Finds project category for the given product id. It compares the given value against id and aliases. + * + * @param {string} productId id of the category. + * @param {boolean} aliasesOnly flag to limit the search to aliases only + * + * @return {object} project category object, from the catalouge, for the given product + */ +export function findProductCategory(productId, aliasesOnly = false) { for(const pType in products) { for(const prd in products[pType].subtypes) { const subType = products[pType].subtypes[prd] - if (subType.id === product && !subType.disabled) { + if (!subType.disabled && ((subType.id === productId && !aliasesOnly) || (subType.aliases && subType.aliases.indexOf(productId) !== -1))) { return { ...products[pType], name: pType} } } diff --git a/src/projects/create/components/ProjectWizard.jsx b/src/projects/create/components/ProjectWizard.jsx index 226d97951..db5f264c0 100644 --- a/src/projects/create/components/ProjectWizard.jsx +++ b/src/projects/create/components/ProjectWizard.jsx @@ -2,7 +2,7 @@ import _ from 'lodash' import { unflatten } from 'flat' import React, { Component, PropTypes } from 'react' -import { findCategory, findProductCategory, findProductsOfCategory, getProjectCreationTemplateField } from '../../../config/projectWizard' +import { findProduct, findCategory, findProductCategory, findProductsOfCategory, getProjectCreationTemplateField } from '../../../config/projectWizard' import Wizard from '../../../components/Wizard' import SelectProjectType from './SelectProjectType' import SelectProduct from './SelectProduct' @@ -62,15 +62,17 @@ class ProjectWizard extends Component { let wizardStep = WZ_STEP_SELECT_PROJ_TYPE if (params && params.product) { // first try the path param to be a project category - let projectType = findCategory(params.product) + let projectType = findCategory(params.product, true) if (projectType) {// if its a category updateQuery['type'] = { $set : projectType.id } wizardStep = WZ_STEP_SELECT_PROD_TYPE } else { // if it is not a category, it should be a product and we should be able to find a category for it - projectType = findProductCategory(params.product) + projectType = findProductCategory(params.product, true) + // finds product object from product alias + const product = findProduct(params.product, true) updateQuery['type'] = { $set : projectType.id } - updateQuery['details'] = { products : { $set: [params.product] } } + updateQuery['details'] = { products : { $set: [product.id] } } wizardStep = WZ_STEP_FILL_PROJ_DETAILS } } @@ -105,16 +107,18 @@ class ProjectWizard extends Component { const updateQuery = {} if (params && params.product) { // if there exists product path param // first try the path param to be a project category - let projectType = findCategory(params.product) + let projectType = findCategory(params.product, true) if (projectType) {// if its a category updateQuery['type'] = { $set : projectType.id } wizardStep = WZ_STEP_SELECT_PROD_TYPE } else { // if it is not a category, it should be a product and we should be able to find a category for it - projectType = findProductCategory(params.product) + projectType = findProductCategory(params.product, true) + // finds product object from product alias + const product = findProduct(params.product, true) if (projectType) {// we can have `incomplete` as params.product updateQuery['type'] = { $set : projectType.id } - updateQuery['details'] = { products : { $set: [params.product] } } + updateQuery['details'] = { products : { $set: [product.id] } } wizardStep = WZ_STEP_FILL_PROJ_DETAILS } } diff --git a/src/projects/create/containers/CreateContainer.jsx b/src/projects/create/containers/CreateContainer.jsx index a14443d1e..718607789 100644 --- a/src/projects/create/containers/CreateContainer.jsx +++ b/src/projects/create/containers/CreateContainer.jsx @@ -7,6 +7,7 @@ import { createProjectWithStatus as createProjectAction, fireProjectDirty, fireP import CoderBot from '../../../components/CoderBot/CoderBot' import spinnerWhileLoading from '../../../components/LoadingSpinner' import ProjectWizard from '../components/ProjectWizard' +import { findProduct, findCategory } from '../../../config/projectWizard' import { CREATE_PROJECT_FAILURE, LS_INCOMPLETE_PROJECT, @@ -145,8 +146,18 @@ class CreateConainer extends React.Component { createProject={ this.createProject } processing={ this.state.creatingProject } onStepChange={ (wizardStep, updatedProject) => { - const projectType = _.get(updatedProject, 'type', null) - const product = _.get(updatedProject, 'details.products[0]', null) + // type of the project + let projectType = _.get(updatedProject, 'type', null) + // finds project category object from the catalogue + const projectCategory = findCategory(projectType) + // updates the projectType variable to use first alias to create SEO friendly URL + projectType = _.get(projectCategory, 'aliases[0]', projectType) + // product of the project + let productType = _.get(updatedProject, 'details.products[0]', null) + // finds product object from the catalogue + const product = findProduct(productType) + // updates the productType variable to use first alias to create SEO friendly URL + productType = _.get(product, 'aliases[0]', productType) if (wizardStep === ProjectWizard.Steps.WZ_STEP_INCOMP_PROJ_CONF) { browserHistory.push(NEW_PROJECT_PATH + '/incomplete') } @@ -156,8 +167,8 @@ class CreateConainer extends React.Component { if (projectType && wizardStep === ProjectWizard.Steps.WZ_STEP_SELECT_PROD_TYPE) { browserHistory.push(NEW_PROJECT_PATH + '/' + projectType + window.location.search) } - if (projectType && product && wizardStep === ProjectWizard.Steps.WZ_STEP_FILL_PROJ_DETAILS) { - browserHistory.push(NEW_PROJECT_PATH + '/' + product + window.location.search) + if (projectType && productType && wizardStep === ProjectWizard.Steps.WZ_STEP_FILL_PROJ_DETAILS) { + browserHistory.push(NEW_PROJECT_PATH + '/' + productType + window.location.search) } this.setState({ wizardStep @@ -171,7 +182,10 @@ class CreateConainer extends React.Component { // compares updated product with previous product to know if user has updated the product if (prevProduct !== product) { if (product) { - browserHistory.push(NEW_PROJECT_PATH + '/' + product + window.location.search) + // intentionally commented because now it should not be require as we handling all URL changes in onStepChange + // earlier we were not getting updated project in onStepChange handler, hence it was required here + // still leaving it here for next few release, in case we find any issue because of commenting this line + // browserHistory.push(NEW_PROJECT_PATH + '/' + product + window.location.search) } } this.setState({ diff --git a/src/projects/list/components/Projects/ProjectsView.jsx b/src/projects/list/components/Projects/ProjectsView.jsx index 5ebad9432..fd27f4556 100644 --- a/src/projects/list/components/Projects/ProjectsView.jsx +++ b/src/projects/list/components/Projects/ProjectsView.jsx @@ -40,7 +40,7 @@ const projectTypeClassMap = { 'app_dev' : 'green-block', 'app' : 'green-block', 'website' : 'green-block', - 'all-chatbots' : 'green-block', + 'chatbot' : 'green-block', 'quality_assurance' : 'green-block' } /*eslint-enable */ diff --git a/src/routes.jsx b/src/routes.jsx index 1d0c76ab2..b12262b26 100644 --- a/src/routes.jsx +++ b/src/routes.jsx @@ -86,9 +86,9 @@ const redirectToProject = (nextState, replace, callback) => { const validateCreateProjectParams = (nextState, replace, callback) => { const product = nextState.params.product // first try the path param to be a project category - let productCategory = findCategory(product) + let productCategory = findCategory(product, true) // if it is not a category, it should be a product and we should be able to find a category for it - productCategory = !productCategory ? findProductCategory(product) : productCategory + productCategory = !productCategory ? findProductCategory(product, true) : productCategory if (product && product.trim().length > 0 && !productCategory) { // workaround to add URL for incomplete project confirmation step // ideally we should have better URL naming which resolves each route with distinct patterns