From 8c301f6e54bd38dadb7d96945938419677063794 Mon Sep 17 00:00:00 2001 From: sanju singh Date: Wed, 25 Dec 2019 13:03:59 +0530 Subject: [PATCH 01/68] Fix: some lines disappearing during editing in editor based on Draftjs - Renders nested code block as inline block except in case of list item it renders code as fence. - In case of multiple paragraph inside list item, only the last one was being considered. Fix this to consider all paragraph inside list item. --- src/helpers/markdownToState.js | 65 ++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/helpers/markdownToState.js b/src/helpers/markdownToState.js index 1641b0bcd..cb6db1ea6 100644 --- a/src/helpers/markdownToState.js +++ b/src/helpers/markdownToState.js @@ -245,6 +245,7 @@ function markdownToState(markdown, options = {}) { const entityMap = {} // entitymap will be returned as part of the final draftjs raw object const parsedData = md.parse(_markdown, {}) // remarkable js takes markdown and makes it an array of style objects for us to easily parse let currentListType = null // Because of how remarkable's data is formatted, we need to cache what kind of list we're currently dealing with + let insideListItem = false // To handle nested code blocks inside list items // Allow user to define custom BlockTypes and Entities if they so wish const BlockTypes = Object.assign({}, DefaultBlockTypes, options.blockTypes || {}) @@ -262,9 +263,32 @@ function markdownToState(markdown, options = {}) { let itemType = item.type if (itemType === 'list_item_open') { itemType = currentListType + insideListItem = true + } else if (itemType === 'list_item_close'){ + insideListItem = false + } + + if (itemType === 'code') { + // If code is at level zero or if code is inside list item, we can treat it similar to fence + // Draftjs doesn't support nested blocks, To handle this we render code as inline + if(item.level === 0 || insideListItem){ + item.type = 'fence' + item.level = 0 // If inside list item, should behave as it is at level 0 + }else{ + // Convert code to inline in cases like: code is inside the blockquote + convertCodeToInline(item) + } + + itemType = item.type } if (itemType === 'inline') { + // Add unstyled block if there is no recently created block exists + // This will ensure we are never overwritting content + if(blocks[blocks.length - 1].text !== ''){ + const block = getNewBlock(BlockTypes['paragraph_open']()) + blocks.push(block) + } // Parse inline content and apply it to the most recently created block level item, // which is where the inline content will belong. const {content, blockEntities, blockEntityRanges, blockInlineStyleRanges} = parseInline(item, BlockEntities, BlockStyles) @@ -282,21 +306,42 @@ function markdownToState(markdown, options = {}) { // List items will always be at least `level==1` though so we need a separate check fo that // TODO: Draft does allow lists to be nested within lists, it's the one exception to its rule, // but right now this code doesn't support that. - if (item.level === 0 || item.type === 'list_item_open') { - const block = Object.assign({ - depth: 0, - // set default values when creating a block, usually the block would have some inline items and - // so these default values would be overwritten because of inline items by `blockToModify` object above - text: '', - inlineStyleRanges: [], - entityRanges: [], - }, BlockTypes[itemType](item)) - + // blockquote may be nested inside list item with level greater than 0 + if (item.level === 0 || item.type === 'list_item_open' || item.type === 'blockquote_open') { + const block = getNewBlock(BlockTypes[itemType](item)) blocks.push(block) } } }) + /** + * Helper function to convert block item of type Code to inline + */ + function convertCodeToInline(item) { + item.type = 'inline' + const codeBlockSixSpace = ' ' + item.content = codeBlockSixSpace + item.content + item.children = [{ + type: 'text', + content: item.content, + level: 0 + }] + } + + /** + * Helper function to create empty block type + */ + function getNewBlock(DefaultBlockType) { + return Object.assign({ + depth: 0, + // set default values when creating a block, usually the block would have some inline items and + // so these default values would be overwritten because of inline items by `blockToModify` object above + text: '', + inlineStyleRanges: [], + entityRanges: [], + }, DefaultBlockType) + } + return convertFromRaw({ entityMap, blocks, From d47ff271b05d60e6079b0d9a69211f8cbf50673c Mon Sep 17 00:00:00 2001 From: Sagar Tewari Date: Wed, 25 Dec 2019 16:12:39 +0530 Subject: [PATCH 02/68] WIP --- package-lock.json | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index db7dff931..6ddc8be5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -412,14 +412,15 @@ } }, "appirio-tech-react-components": { - "version": "git+https://github.com/vigneshTheDev/react-components.git#edd4c4bd353b08937b1f59099129b685f9145683", - "from": "git+https://github.com/vigneshTheDev/react-components.git#feature/connectv2", + "version": "git+https://github.com/appirio-tech/react-components.git#f5a256f0d871764d53fa5ec3eb724caeea7f831a", + "from": "git+https://github.com/appirio-tech/react-components.git#feature/connectv2", "requires": { "appirio-tech-api-schemas": "^5.0.69", "appirio-tech-client-app-layer": "^0.1.3", "classnames": "^2.2.3", "coffee-script": "^1.12.7", "coffeescript": "^1.12.7", + "filestack-js": "^1.13.2", "formsy-react": "^0.19.5", "isomorphic-fetch": "^2.2.1", "libphonenumber-js": "^1.4.6", @@ -427,6 +428,7 @@ "material-ui": "^0.20.2", "moment": "^2.11.2", "prop-types": "^15.7.2", + "rc-slider": "8.6.4", "react": "^15.3.1", "react-addons-pure-render-mixin": "^15.3.1", "react-addons-update": "^15.3.1", @@ -435,6 +437,7 @@ "react-dom": "^15.3.1", "react-dropzone": "^3.5.3", "react-popper": "^0.7.5", + "react-portal": "^4.2.0", "react-redux": "^4.4.5", "react-router-dom": "^4.2.2", "react-select": "^0.9.1", @@ -442,7 +445,7 @@ "react-textarea-autosize": "^5.2.1", "react-transition-group": "^2.2.1", "redux-thunk": "^2.1.0", - "tc-ui": "git+https://github.com/appirio-tech/tc-ui.git#feature/connectv2", + "tc-ui": "git+https://github.com/appirio-tech/tc-ui.git#e577a0e704136f1e9ecce92ce4c0626aab932691", "uncontrollable": "^4.0.1" }, "dependencies": { @@ -8755,9 +8758,9 @@ } }, "libphonenumber-js": { - "version": "1.7.25", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.7.25.tgz", - "integrity": "sha512-WlmFl3xSR0oGH0+ZGtEbcNgo6h7JUyYaRmGWlddrkEFmPjAULRur2KZzmNWQSBfD0LhUdnKYhwIi2MnB7gswWw==", + "version": "1.7.29", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.7.29.tgz", + "integrity": "sha512-k0qJpAQ6DZA3b6meri1CZr2Lg1lvC539wqLljFdCWLPK8jAXLFqE433NzGzTdxKfm/EmGE8Zhm5pK8R9N7m1pg==", "requires": { "minimist": "^1.2.0", "xml2js": "^0.4.17" @@ -13967,6 +13970,14 @@ } } }, + "react-portal": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/react-portal/-/react-portal-4.2.0.tgz", + "integrity": "sha512-Zf+vGQ/VEAb5XAy+muKEn48yhdCNYPZaB1BWg1xc8sAZWD8pXTgPtQT4ihBdmWzsfCq8p8/kqf0GWydSBqc+Eg==", + "requires": { + "prop-types": "^15.5.8" + } + }, "react-prop-types": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/react-prop-types/-/react-prop-types-0.4.0.tgz", @@ -17310,7 +17321,7 @@ "Base64": "~0.1.3", "json-fallback": "0.0.1", "jsonp": "~0.0.4", - "qs": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays", + "qs": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8", "reqwest": "^1.1.4", "trim": "~0.0.1", "winchan": "^0.1.1", From 7e2e631cb01814b116e69eca421c7f49c416f6a2 Mon Sep 17 00:00:00 2001 From: Sagar Tewari Date: Wed, 25 Dec 2019 16:46:13 +0530 Subject: [PATCH 03/68] almost done --- src/components/TeamManagement/MemberItem.jsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/components/TeamManagement/MemberItem.jsx b/src/components/TeamManagement/MemberItem.jsx index ac818632d..a5e7f3764 100644 --- a/src/components/TeamManagement/MemberItem.jsx +++ b/src/components/TeamManagement/MemberItem.jsx @@ -1,7 +1,6 @@ import React from 'react' import PropTypes from 'prop-types' import moment from 'moment' -import {timezones} from 'appirio-tech-react-components/constants/timezones' import UserTooltip from '../User/UserTooltip' import SunIcon from '../../assets/icons/daylight.svg' import MoonIcon from '../../assets/icons/moon.svg' @@ -19,16 +18,12 @@ const MemberItem = (props) => { const email = _.get(usr, 'email') let localTime let localTimeOffsetFormat - let timeZoneInfo + let utcOff if(timeZone) { - timeZoneInfo = _.find(timezones, (t) => {return t.zoneName === timeZone}) - // as a quick fix for https://github.com/appirio-tech/connect-app/issues/3457 - // set offset for the "Daylight Saving Time" - if (timeZoneInfo.zoneName === 'America/New_York') { - timeZoneInfo.gmtOffset = -18000 - } - localTime = moment().utcOffset(timeZoneInfo.gmtOffset/3600).format('h:mm a') - localTimeOffsetFormat = 'UTC' + moment().utcOffset(timeZoneInfo.gmtOffset/3600).format('Z') + const tz = moment().tz(timeZone) + localTime = tz.format('h:mm a') + utcOff = Math.round(tz.utcOffset()/60) + localTimeOffsetFormat = 'UTC' + moment().utcOffset(utcOff).format('Z') } let localTimeInfoEl = null @@ -45,7 +40,7 @@ const MemberItem = (props) => { localWhEnd = moment({hour: workingHourEnd.split(':')[0]}).format('h a') if(localTime) { - let localHour = +moment().utcOffset(timeZoneInfo.gmtOffset/3600).format('H') + let localHour = +moment().utcOffset(utcOff).format('H') const localStartHour = +moment({hour: workingHourStart.split(':')[0] }).format('H') let localEndHour = +moment({hour: workingHourEnd.split(':')[0] }).format('H') if(localEndHour <= localStartHour) { From 493389a42291af8b7b1cb863cfb9bfb8073f4b1f Mon Sep 17 00:00:00 2001 From: Sagar Tewari Date: Wed, 25 Dec 2019 16:53:17 +0530 Subject: [PATCH 04/68] require moment-timezone --- package-lock.json | 60 ++++++++------------ package.json | 1 + src/components/TeamManagement/MemberItem.jsx | 1 + 3 files changed, 25 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6ddc8be5b..5d84139a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6077,8 +6077,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6099,14 +6098,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6121,20 +6118,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6251,8 +6245,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6264,7 +6257,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6279,7 +6271,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6287,14 +6278,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6313,7 +6302,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6394,8 +6382,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6407,7 +6394,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6493,8 +6479,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6530,7 +6515,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6550,7 +6534,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6594,14 +6577,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9692,6 +9673,14 @@ "es6-symbol": "^3.1.0" } }, + "moment-timezone": { + "version": "0.5.27", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz", + "integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==", + "requires": { + "moment": ">= 2.9.0" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -17321,17 +17310,10 @@ "Base64": "~0.1.3", "json-fallback": "0.0.1", "jsonp": "~0.0.4", - "qs": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8", "reqwest": "^1.1.4", "trim": "~0.0.1", "winchan": "^0.1.1", "xtend": "~2.1.1" - }, - "dependencies": { - "qs": { - "version": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8", - "from": "git+https://github.com/jfromaniello/node-querystring.git#fix_ie7_bug_with_arrays" - } } }, "auto-config-fake-server": { @@ -23382,6 +23364,10 @@ "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", "integrity": "sha512-VVMcd+HnuWZalHPycK7CsbVJ+sSrrrnCvHcW38YJVK9Tywnb5DUWJjONi81bLUj7aqDjIXnePxBl5t1r/F/ncg==" }, + "qs": { + "version": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8", + "from": "git+https://github.com/jfromaniello/node-querystring.git#5d96513991635e3e22d7aa54a8584d6ce97cace8" + }, "query-string": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", diff --git a/package.json b/package.json index 4b56cc7ea..f3d7ca668 100644 --- a/package.json +++ b/package.json @@ -109,6 +109,7 @@ "mobile-detect": "^1.4.2", "moment": "^2.14.1", "moment-range": "^4.0.1", + "moment-timezone": "^0.5.27", "prop-types": "^15.6.0", "query-string": "^4.3.4", "rc-slider": "8.6.4", diff --git a/src/components/TeamManagement/MemberItem.jsx b/src/components/TeamManagement/MemberItem.jsx index a5e7f3764..37a08033f 100644 --- a/src/components/TeamManagement/MemberItem.jsx +++ b/src/components/TeamManagement/MemberItem.jsx @@ -1,6 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' import moment from 'moment' +require('moment-timezone') import UserTooltip from '../User/UserTooltip' import SunIcon from '../../assets/icons/daylight.svg' import MoonIcon from '../../assets/icons/moon.svg' From 4ad88bc7ec95bf988a4ec40a785a4c9ec3a24296 Mon Sep 17 00:00:00 2001 From: Sagar Tewari Date: Wed, 25 Dec 2019 17:04:32 +0530 Subject: [PATCH 05/68] don't round hour --- src/components/TeamManagement/MemberItem.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/TeamManagement/MemberItem.jsx b/src/components/TeamManagement/MemberItem.jsx index 37a08033f..8e3c284c2 100644 --- a/src/components/TeamManagement/MemberItem.jsx +++ b/src/components/TeamManagement/MemberItem.jsx @@ -23,7 +23,7 @@ const MemberItem = (props) => { if(timeZone) { const tz = moment().tz(timeZone) localTime = tz.format('h:mm a') - utcOff = Math.round(tz.utcOffset()/60) + utcOff = tz.utcOffset()/60 localTimeOffsetFormat = 'UTC' + moment().utcOffset(utcOff).format('Z') } let localTimeInfoEl = null From adac6b9bc2569c483fa2d889d0ad076d47eed9f3 Mon Sep 17 00:00:00 2001 From: yoution Date: Wed, 8 Jan 2020 01:39:42 +0800 Subject: [PATCH 06/68] New Fields for Project Templates --- src/routes/metadata/components/MetaDataPanel.jsx | 4 ++++ src/routes/metadata/components/TemplateForm.jsx | 13 +++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/routes/metadata/components/MetaDataPanel.jsx b/src/routes/metadata/components/MetaDataPanel.jsx index 75d46544b..a27760215 100644 --- a/src/routes/metadata/components/MetaDataPanel.jsx +++ b/src/routes/metadata/components/MetaDataPanel.jsx @@ -369,16 +369,20 @@ class MetaDataPanel extends React.Component { } else if (metadataType === 'projectTemplate') { const projectTypeOptions = this.getProductCategoryOptions(templates.projectTypes) const value = metadata && metadata.category ? metadata.category : projectTypeOptions[0].value + const subCategoryVal = metadata && metadata.subCategory ? metadata.subCategory : '' + const metadataVal = metadata && metadata.metadataVal ? metadata.metadataVal : {} fields = fields.concat([ { key: 'id', type: 'number' }, { key: 'name', type: 'text' }, { key: 'key', type: 'text' }, { key: 'category', type: 'dropdown', options: projectTypeOptions, value }, + { key: 'subCategory', type: 'dropdown', options: projectTypeOptions.concat({title: '--', value: ''}), value: subCategoryVal }, { key: 'icon', type: 'text' }, { key: 'question', type: 'text' }, { key: 'info', type: 'text' }, { key: 'aliases', type: 'array' }, { key: 'phases', type: 'json', value: phasesDefaultValue }, + { key: 'metadata', type: 'json', value: metadataVal }, { key: 'disabled', type: 'checkbox' }, { key: 'hidden', type: 'checkbox' }, ]) diff --git a/src/routes/metadata/components/TemplateForm.jsx b/src/routes/metadata/components/TemplateForm.jsx index acfd340ca..bef21a8c7 100644 --- a/src/routes/metadata/components/TemplateForm.jsx +++ b/src/routes/metadata/components/TemplateForm.jsx @@ -165,10 +165,10 @@ class TemplateForm extends Component { theme="default max-height" onSelect={(option) => { this.props.dropdownChange(label, option) - this.onChangeDropdown(option) + this.onChangeDropdown(label, option) }} value={value} - required + required={label!=='subCategory'} /> ) @@ -284,6 +284,9 @@ class TemplateForm extends Component { const aliases = _.split(values.aliases, ',') payload = _.assign({}, payload, { aliases }) } + if(!payload.subCategory) { + payload.subCategory = null + } saveTemplate(primaryKeyValue, payload) } @@ -312,10 +315,12 @@ class TemplateForm extends Component { } } - onChangeDropdown(option) { + onChangeDropdown(label, option) { const { values } = this.state + const updatedValue = {} + updatedValue[`${label}`] = option.value this.setState({ - values: _.assign({}, values, {category: option.value}) + values: _.assign({}, values, updatedValue) }) } From 769da9bcb6d35cffd1c58aa28f9ba04d701759bf Mon Sep 17 00:00:00 2001 From: Maksym Mykhailenko Date: Sat, 11 Jan 2020 12:15:15 +0800 Subject: [PATCH 07/68] fix: saving non-project-templates metadata forms --- src/routes/metadata/components/TemplateForm.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/metadata/components/TemplateForm.jsx b/src/routes/metadata/components/TemplateForm.jsx index bef21a8c7..d00e5ccb9 100644 --- a/src/routes/metadata/components/TemplateForm.jsx +++ b/src/routes/metadata/components/TemplateForm.jsx @@ -284,7 +284,7 @@ class TemplateForm extends Component { const aliases = _.split(values.aliases, ',') payload = _.assign({}, payload, { aliases }) } - if(!payload.subCategory) { + if (payload.subCategory === '') { payload.subCategory = null } saveTemplate(primaryKeyValue, payload) From e50b604ea3e42aa511b98ca35d90bae067b9b941 Mon Sep 17 00:00:00 2001 From: dat Date: Wed, 15 Jan 2020 04:17:14 +0700 Subject: [PATCH 08/68] code 30112625 code 30112625 --- .../icons/ico-mobile-search-selected.svg | 15 + .../images/curve-horizontal-2100x78.png | Bin 0 -> 2788 bytes src/assets/images/curve-vertical-43x100.png | Bin 0 -> 2290 bytes .../WizardWrapper/WizardWrapper.scss | 1 + src/helpers/templates.js | 14 + src/helpers/utils.js | 16 + .../components/SelectProjectTemplate.jsx | 286 ++++++++++++++--- .../components/SelectProjectTemplate.scss | 199 ++++++++++-- .../components/SelectProjectTemplateCard.js | 101 ++++++ .../components/SelectProjectTemplateCard.scss | 299 ++++++++++++++++++ .../create/components/SelectProjectType.jsx | 16 +- .../create/components/SelectProjectType.scss | 45 ++- .../components/SelectProjectTypeCard.jsx | 35 ++ .../components/SelectProjectTypeCard.scss | 46 +++ src/projects/create/components/_layout.scss | 8 +- 15 files changed, 1005 insertions(+), 76 deletions(-) create mode 100644 src/assets/icons/ico-mobile-search-selected.svg create mode 100644 src/assets/images/curve-horizontal-2100x78.png create mode 100644 src/assets/images/curve-vertical-43x100.png create mode 100644 src/projects/create/components/SelectProjectTemplateCard.js create mode 100644 src/projects/create/components/SelectProjectTemplateCard.scss create mode 100644 src/projects/create/components/SelectProjectTypeCard.jsx create mode 100644 src/projects/create/components/SelectProjectTypeCard.scss diff --git a/src/assets/icons/ico-mobile-search-selected.svg b/src/assets/icons/ico-mobile-search-selected.svg new file mode 100644 index 000000000..d9c6729f3 --- /dev/null +++ b/src/assets/icons/ico-mobile-search-selected.svg @@ -0,0 +1,15 @@ + + + + ico-mobile-search-selected + Created with Sketch. + + + + + + + + + + diff --git a/src/assets/images/curve-horizontal-2100x78.png b/src/assets/images/curve-horizontal-2100x78.png new file mode 100644 index 0000000000000000000000000000000000000000..e8d7e7145cecb4380b9a5a97da4c230cc89e566f GIT binary patch literal 2788 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 Z4ag#oi@sNH{@M2mNt32a>O@M%e*u%MXchnf literal 0 HcmV?d00001 diff --git a/src/components/WizardWrapper/WizardWrapper.scss b/src/components/WizardWrapper/WizardWrapper.scss index a28f929c9..6ec5537f1 100644 --- a/src/components/WizardWrapper/WizardWrapper.scss +++ b/src/components/WizardWrapper/WizardWrapper.scss @@ -39,4 +39,5 @@ flex: 1 1 auto; background: $tc-gray-0; box-shadow: 0 0 5px 0 #C0C0C0, 0 0 1px 0 #D5D5D5; + max-width: 100%; } diff --git a/src/helpers/templates.js b/src/helpers/templates.js index d60c629f7..82974782d 100644 --- a/src/helpers/templates.js +++ b/src/helpers/templates.js @@ -136,6 +136,20 @@ export function getProjectTemplatesByCategory(projectTemplates, categoryKey, vis .filter((projectTemplate) => visibleOnly ? !projectTemplate.hidden && !projectTemplate.disabled : true) } +/** + * Get project templates by sub category + * + * @param {Array} projectTemplates list of project templates + * @param {String} categoryKey category key + * @param {Boolean} visibleOnly if true only not hidden and not disabled project templates will returned + * + * @returns {Array} list of project templates + */ +export function getProjectTemplatesBySubCategory(projectTemplates, categoryKey, visibleOnly) { + return _.filter(projectTemplates, { subCategory: categoryKey }) + .filter((projectTemplate) => visibleOnly ? !projectTemplate.hidden && !projectTemplate.disabled : true) +} + /** * Get project type by its key * diff --git a/src/helpers/utils.js b/src/helpers/utils.js index d38b72ac5..c90c19b3f 100644 --- a/src/helpers/utils.js +++ b/src/helpers/utils.js @@ -113,3 +113,19 @@ export const createEvent = (eventName) => { export const delay = (milliseconds) => new Promise((resolve) => { setTimeout(resolve, milliseconds) }) + +/** + * Case-insensitive search + * + * @param {String} key search key + * @param {String} searchString search string + * + * @returns {Bool} + */ +export const caseInsensitiveSearch = (key, searchString) => { + if (!key || !searchString) { + return false + } + + return searchString.toLowerCase().indexOf(key.toLowerCase()) >= 0 +} diff --git a/src/projects/create/components/SelectProjectTemplate.jsx b/src/projects/create/components/SelectProjectTemplate.jsx index 5f9f4d0b2..be54ed220 100644 --- a/src/projects/create/components/SelectProjectTemplate.jsx +++ b/src/projects/create/components/SelectProjectTemplate.jsx @@ -1,54 +1,260 @@ import React from 'react' import PT from 'prop-types' +import cn from 'classnames' -import ProductCard from './ProductCard' +import SelectProjectTemplateCard from './SelectProjectTemplateCard' import ProjectTypeIcon from '../../../components/ProjectTypeIcon' -import { getProjectTypeByKey, getProjectTemplatesByCategory } from '../../../helpers/templates' +import IconArrowRight from '../../../assets/icons/arrows-16px-1_tail-right.svg' + +import { + getProjectTypeByKey, + getProjectTemplatesByCategory, + getProjectTemplatesBySubCategory +} from '../../../helpers/templates' + +import { + caseInsensitiveSearch +} from '../../../helpers/utils' import { DOMAIN } from '../../../config/constants' +import SearchIcon from '../../../assets/icons/ico-mobile-search-selected.svg' +import CloseSearchIcon from '../../../assets/icons/icon-x-mark.svg' import './SelectProjectTemplate.scss' -const SelectProjectTemplate = ({ - onProjectTemplateChange, - projectTemplates, - projectTypeKey, - projectTypes, -}) => { - const projectType = getProjectTypeByKey(projectTypes, projectTypeKey) - const visibleProjectTemplates = getProjectTemplatesByCategory(projectTemplates, projectTypeKey, true) - const cards = [] - - visibleProjectTemplates.forEach((projectTemplate) => { - // don't render disabled items for selection - // don't render hidden items as well, hidden items can be reached via direct link though - if (projectTemplate.disabled || projectTemplate.hidden) return - - const icon = - - cards.push( - onProjectTemplateChange(projectTemplate)} - type={projectTemplate.name} - /> - ) - }) - - return ( -
-
-

{ projectType.displayName } projects

-

{ projectType.question }

-
{cards}
-
- Looking for something else? Get in touch with us → + +class SelectProjectTemplate extends React.Component{ + constructor(props) { + super(props) + this.state = { + selectedGroup: { + displayName: 'All Solutions', + }, + searchInputValue: '', + selectedSearchInputValue: '', + } + + this.getProjectType = this.getProjectType.bind(this) + this.getFilterList = this.getFilterList.bind(this) + this.getVisibleProjectTemplatesGroup = this.getVisibleProjectTemplatesGroup.bind(this) + this.canFilter = this.canFilter.bind(this) + } + + /** + * Get current project type + */ + getProjectType() { + const { + projectTypeKey, + projectTypes, + } = this.props + + return getProjectTypeByKey(projectTypes, projectTypeKey) + } + + /** + * Get filter list + */ + getFilterList() { + const filterList = [{ + displayName: 'All Solutions', + }] + + const { + projectTypes, + projectTemplates, + projectTypeKey, + } = this.props + + const availableProjectTemplates = getProjectTemplatesByCategory(projectTemplates, projectTypeKey, true) + + _.each(projectTypes, (pT) => { + const visibleProjectTemplates = getProjectTemplatesBySubCategory(availableProjectTemplates, pT.key, true) + if (visibleProjectTemplates.length > 0) { + const projectType = _.cloneDeep(pT) + projectType.visibleProjectTemplates = visibleProjectTemplates + filterList.push(projectType) + } + }) + + return filterList + } + + /** + * get visible project templates by group + */ + getVisibleProjectTemplatesGroup() { + const { + projectTemplates, + projectTypeKey, + onProjectTemplateChange, + } = this.props + + const { + selectedGroup, + selectedSearchInputValue + } = this.state + + const getCardsUI = (visibleProjectTemplates) => { + const cards = [] + visibleProjectTemplates.forEach((projectTemplate) => { + // don't render disabled items for selection + // don't render hidden items as well, hidden items can be reached via direct link though + if (projectTemplate.disabled || projectTemplate.hidden) return + + if (selectedSearchInputValue) { + let isMatch = false + if ( + caseInsensitiveSearch(selectedSearchInputValue, projectTemplate.name) || + caseInsensitiveSearch(selectedSearchInputValue, projectTemplate.info) + ) { + isMatch = true + } else if (projectTemplate.metadata.deliverables) { + _.each(projectTemplate.metadata.deliverables, (d) => { + if (caseInsensitiveSearch(selectedSearchInputValue, d.infoHTML)) { + isMatch = true + } + }) + } + if (!isMatch) { + return + } + } + + const icon = + cards.push( + onProjectTemplateChange(projectTemplate)} + /> + ) + }) + return cards + } + + if (!this.canFilter()) { + return [ + { + displayName: null, + info: null, + cards: getCardsUI(getProjectTemplatesByCategory(projectTemplates, projectTypeKey, true)) + } + ] + } + const groups = [] + + _.each(this.getFilterList(), (filterGroup) => { + if (filterGroup.displayName !== 'All Solutions') { + if (selectedGroup.displayName === 'All Solutions') { + groups.push({ + displayName: filterGroup.displayName, + info: filterGroup.info, + cards: getCardsUI(filterGroup.visibleProjectTemplates) + }) + } else if (selectedGroup.displayName === filterGroup.displayName) { + groups.push({ + displayName: filterGroup.displayName, + info: filterGroup.info, + cards: getCardsUI(filterGroup.visibleProjectTemplates) + }) + } + } + } ) + return groups + } + + /** + * Check if can filter + */ + canFilter() { + const projectType = this.getProjectType() + return projectType.metadata.filterable !== false + } + + render() { + const { + selectedGroup, + searchInputValue, + selectedSearchInputValue, + } = this.state + const projectType = this.getProjectType() + return ( +
+
+

{ projectType.metadata.pageHeader }

+

{ projectType.metadata.pageInfo }

+
+ {this.canFilter() && ( +
+ {this.getFilterList().map( + (item, i) => + ( +
{ this.setState({ selectedGroup: item }) }} + key={i} + > + {item.displayName} +
) + )} +
+ )} + {this.canFilter() && ( +
+
+ { this.setState({ searchInputValue: evt.target.value }) }} + /> + {(searchInputValue || selectedSearchInputValue) && ( + + )} +
+ +
+ )} + {this.getVisibleProjectTemplatesGroup().map( + (templateGroup, i) => + (templateGroup.cards.length > 0) && ( +
+ {templateGroup.displayName && ( + {templateGroup.displayName} + )} + {templateGroup.info && ( + {templateGroup.info} + )} + {templateGroup.cards} +
+ ) + )} +
+
+ Looking for something else? Get in touch with us +
-
- ) + ) + } } SelectProjectTemplate.propTypes = { diff --git a/src/projects/create/components/SelectProjectTemplate.scss b/src/projects/create/components/SelectProjectTemplate.scss index 5fb9fee2e..94e37cc9a 100644 --- a/src/projects/create/components/SelectProjectTemplate.scss +++ b/src/projects/create/components/SelectProjectTemplate.scss @@ -7,30 +7,113 @@ .SelectProjectTemplate { @extend .wizardCardContainer; + display: flex; + flex-direction: column; + min-height: 100vh; + background: $tc-gray-neutral-dark; + max-width: none; + width: 100%; + padding-bottom: 0; + align-items: center; + @media screen and (max-width: $screen-md - 1px) { padding: 0 2 * $base-unit; } + .cards { + flex-direction: column; + align-items: center; + padding-left: 30px; + padding-right: 30px; + margin-bottom: 50px; + max-width: 1110px; + width: 100%; + + @media screen and (max-width: $screen-sm - 1px) { + padding-left: 20px; + padding-right: 20px; + } + + .card-section { + display: flex; + flex-direction: column; + margin-bottom: 52px; + width: 100%; + + &:last-child { + margin-bottom: 0; + } + + .card-section-title { + @include roboto-bold; + + text-align: left; + margin-bottom: 4px; + text-transform: uppercase; + color: $tc-gray-90; + font-size: 24px; + line-height: 30px; + } + + .card-section-sub-title { + text-align: left; + margin-bottom: 27px; + text-transform: uppercase; + color: $tc-gray-40; + font-size: 10px; + letter-spacing: 0.8px; + line-height: 22px; + text-align: left; + } + } + } + + .footer { + position: relative; + margin-top: auto; + display: flex; + flex-wrap: wrap; + + a { + display: flex; + align-items: center; + + svg { + margin-left: 7px; + + path { + fill: $tc-dark-blue-100; + } + } + } + } + h1 { @extend .wizardHeadline; - margin-top: 30px; + @include roboto-medium; + + color: $tc-gray-90; + margin-top: 41px; + font-size: 34px; + line-height: 41px; + margin-bottom: 11px; + text-transform: uppercase; @media screen and (max-width: $screen-md - 1px) { font-size: 20px; line-height: 30px; margin-bottom: 0; margin-top: 20px; - padding: 5px 40px 0; + padding: 5px 50px 0; } + h2 { - font-size: 15px; - line-height: 20px; @include tc-heading-small; - @include roboto-bold; + color: $tc-gray-80; - margin-bottom: 13px; - margin-top: 0px; + font-size: 16px; + line-height: 19px; + margin-bottom: 42px; @media screen and (max-width: $screen-md - 1px) { @include roboto-medium; @@ -40,26 +123,96 @@ } } - .header { - display: block; - } - } + .project-type-group { + display: flex; + flex-wrap: wrap; + justify-content: center; + margin-bottom: 25px; + width: 100%; + padding: 0 25px; - @media screen and (max-width: 1100px) { - .SelectProjectTemplate { - max-width: 750px; - } - } + .item { + color: $tc-gray-40; + font-size: 12px; + line-height: 14px; + margin: 0 5px 5px; + padding: 0 15px; + height: 30px; + border-radius: 15px; + display: flex; + align-items: center; + cursor: pointer; - @media screen and (max-width: 850px) { - .SelectProjectTemplate { - max-width: 500px; + &.selected { + background-color: $tc-gray-10; + box-shadow: inset 0 0 2px 0 rgba($tc-black, 0.25); + } + } } - } - @media screen and (max-width: $screen-md - 1px) { - .SelectProjectTemplate { - max-width: 100%; + .search-container { + display: flex; + width: 100%; + margin-bottom: 37px; + + .tc-btn.tc-btn-md { + height: 40px; + + .search-icon { + width: 16px; + margin-right: 5px; + margin-top: 2px; + } + } + + .search-bar { + display: flex; + flex: 1; + margin-right: 10px; + position: relative; + + .close-search-icon { + border: none; + background: transparent; + background-color: $tc-gray-10; + border-radius: 100%; + width: 20px; + height: 20px; + display: flex; + justify-content: center; + align-items: center; + position: absolute; + right: 13px; + top: 10px; + + svg { + width: 7px; + height: 7px; + + path { + fill: $tc-gray-70; + } + } + } + + input { + height: 40px; + line-height: 40px; + padding: 0 40px 0 20px; + border: 1px solid $tc-gray-10; + border-radius: 20px; + font-size: 14px; + margin: 0; + + @include placeholder { + color: $tc-gray-70; + font-size: 14px; + line-height: 40px; + opacity: 0.5; + text-transform: none; + } + } + } } } } diff --git a/src/projects/create/components/SelectProjectTemplateCard.js b/src/projects/create/components/SelectProjectTemplateCard.js new file mode 100644 index 000000000..fdfe9e88f --- /dev/null +++ b/src/projects/create/components/SelectProjectTemplateCard.js @@ -0,0 +1,101 @@ +import _ from 'lodash' +import React from 'react' +import PT from 'prop-types' +import cn from 'classnames' + +import './SelectProjectTemplateCard.scss' +import curveVertical from '../../../assets/images/curve-vertical-43x100.png' +import curveHorizontal from '../../../assets/images/curve-horizontal-2100x78.png' +import IconArrowRight from '../../../assets/icons/arrows-16px-1_tail-right.svg' + +function SelectProjectTemplateCard(p) { + const { projectTemplate } = p + + const subTextHTML = projectTemplate.metadata.subTextHTML + const detailLink = projectTemplate.metadata.detailLink + const deliverables = projectTemplate.metadata.deliverables || [] + + return ( +
1, + })} + > +
+
+
{p.icon}
+
+

{projectTemplate.name}

+
{projectTemplate.info}
+
+ {subTextHTML && ( +
+ )} + {detailLink && ( + Learn More + )} +
+
+
+
+ vertical curve + {(deliverables.length === 1) && ( +
+ )} + +
+
+ {(deliverables.length > 0) && ( +
+ horizontal curve +
+ {deliverables.map( + (item, i) => + ( +
+
+ {item.subTextHTML && ( +
+ )} + +
+ ) + )} +
+
+ )} +
+ ) +} + +SelectProjectTemplateCard.defaultTypes = { + onClick: _.noop +} + +SelectProjectTemplateCard.propTypes = { + icon: PT.element.isRequired, + onClick: PT.func, + projectTemplate: PT.any.isRequired +} + +export default SelectProjectTemplateCard diff --git a/src/projects/create/components/SelectProjectTemplateCard.scss b/src/projects/create/components/SelectProjectTemplateCard.scss new file mode 100644 index 000000000..37c8d22b4 --- /dev/null +++ b/src/projects/create/components/SelectProjectTemplateCard.scss @@ -0,0 +1,299 @@ +@import '~tc-ui/src/styles/tc-includes'; + +.SelectProjectTemplateCard { + background: $tc-white; + border-radius: 10px; + margin: 0 0 30px; + width: 100%; + display: flex; + flex-direction: column; + color: $tc-gray-90; + overflow: hidden; + + &:last-child { + margin-bottom: 0; + } +} + +.one-deliverable { + .bottom-card { + display: none; + + @media screen and (max-width: $screen-md - 1px) { + display: flex; + + .bottom-column { + background: transparent; + padding: 0; + + h3 { + color: $tc-gray-70; + text-transform: none; + display: flex; + justify-content: flex-start; + } + button { + display: flex; + } + } + } + } +} + +.more-deliverable, +.no-deliverable { + .right-card { + flex: 0; + } +} + +.no-deliverable { + .bottom-card { + display: none; + } +} + +.more-deliverable { + .right-card { + background: transparent; + + .vertical-curve, + .info-html { + display: none; + } + } +} + +.header { + @include roboto-bold; + + font-size: 16px; + line-height: 19px; + text-align: left; +} + +.details { + color: $tc-gray-70; + font-size: 14px; + line-height: 17px; + width: 100%; + text-align: left; + margin-top: 10px; + margin-bottom: 21px; +} + +.icon-wrapper { + margin-right: 26px; +} + +.left-card { + flex: 1; + display: flex; + align-items: center; + padding: 35px 30px 36px 45px; + + @media screen and (max-width: $screen-md - 1px) { + flex-direction: column; + text-align: center; + + .left-card-content { + align-items: center; + width: 100%; + } + + .icon-wrapper { + margin-right: 0; + margin-bottom: 20px; + } + } +} + +.right-card { + position: relative; + display: flex; + background: $tc-gray-neutral-light; + align-items: center; + padding: 35px 50px 30px 50px; + flex: 1; + + .info-html { + margin-right: 22px; + } + + @media screen and (max-width: $screen-md - 1px) { + flex: 0; + background: transparent; + + .vertical-curve, + .info-html { + display: none; + } + } + + @media screen and (max-width: $screen-sm - 1px) { + padding: 20px; + } +} + +.top-card { + display: flex; + flex-wrap: wrap; + justify-content: center; + + @media screen and (max-width: $screen-sm - 1px) { + flex-direction: column; + + .right-card { + justify-content: center; + padding-top: 0; + } + } +} + +.left-card-content { + flex: 1; + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.bottom-card { + position: relative; + display: flex; + flex-direction: column; + background: $tc-gray-neutral-light; +} + +.vertical-curve { + height: 100%; + position: absolute; + left: 0; + top: 0; + width: 20px; +} + +.horizontal-curve { + width: 100%; + height: 30px; +} + +.learn-more-container { + display: flex; + width: 100%; + justify-content: space-between; + font-size: 14px; + letter-spacing: 0.5px; + line-height: 17px; + flex-wrap: wrap; + align-items: center; +} + +.starting-at { + font-size: 14px; + color: $tc-gray-90; + + strong { + @include roboto-bold; + + margin-left: 4px; + } +} + +.learn-more { + @include roboto-medium; + + font-size: 15px; + letter-spacing: 1px; + line-height: 18px; + text-transform: uppercase; + display: flex; + align-items: center; + + svg { + margin-left: 7px; + + path { + fill: $tc-dark-blue-100; + } + } +} + +.info-html { + text-align: left; + + h3 { + @include roboto-bold; + + min-height: 30px; + margin-bottom: 10px; + display: flex; + align-items: center; + } + + * { + color: $tc-gray-70; + font-size: 14px; + line-height: 17px; + } + + li { + position: relative; + padding-left: 15px; + margin-bottom: 5px; + + &::before { + content: ""; + display: flex; + position: absolute; + width: 5px; + height: 5px; + background: $tc-dark-blue-100; + border-radius: 100%; + top: 7px; + left: 0; + } + } +} + +.bottom-column { + display: flex; + flex-direction: column; + width: 100%; + margin: 14px; + background: $tc-white; + border-radius: 10px; + max-width: 305px; + justify-content: space-between; + padding: 25px 30px 26px; + + @media screen and (max-width: $screen-sm - 1px) { + margin-left: 0; + margin-right: 0; + } + + .starting-at { + margin-top: 16px; + } + + .info-html { + h3 { + color: $tc-gray-90; + display: flex; + justify-content: center; + text-transform: uppercase; + } + } + + button { + display: none; + align-self: center; + margin-top: 30px; + } +} + +.bottom-card-content { + display: flex; + justify-content: center; + flex-wrap: wrap; + padding: 16px 25px 27px 26px; +} diff --git a/src/projects/create/components/SelectProjectType.jsx b/src/projects/create/components/SelectProjectType.jsx index 329281c1d..7d2627598 100644 --- a/src/projects/create/components/SelectProjectType.jsx +++ b/src/projects/create/components/SelectProjectType.jsx @@ -1,9 +1,10 @@ import React from 'react' import PT from 'prop-types' -import ProjectTypeCard from './ProjectTypeCard' +import SelectProjectTypeCard from './SelectProjectTypeCard' import { getProjectTemplatesByCategory } from '../../../helpers/templates' import ProjectTypeIcon from '../../../components/ProjectTypeIcon' +import IconArrowRight from '../../../assets/icons/arrows-16px-1_tail-right.svg' import { DOMAIN } from '../../../config/constants' @@ -17,22 +18,23 @@ const SelectProjectType = ({ const cards = [] projectTypes.forEach((projectType) => { - // don't render disabled items for selection - // don't render hidden items as well, hidden items can be reached via direct link though - if (projectType.disabled || projectType.hidden) return const visibleProjectTemplates = getProjectTemplatesByCategory(projectTemplates, projectType.key, true) + // don't render disabled items for selection + // don't render hidden items as well, hidden items can be reached via direct link though + if (projectType.disabled || projectType.hidden || visibleProjectTemplates.length === 0) return + const icon = cards.push( - onProjectTypeChange(projectType.key)} type={projectType.displayName} - buttonText={visibleProjectTemplates.length > 1 ? 'View All' : 'Select Project'} + buttonText="View Solutions" /> ) }) @@ -43,7 +45,7 @@ const SelectProjectType = ({

Create a new project

{cards}
- Looking for something else? Get in touch with us → + Looking for something else? Get in touch with us
diff --git a/src/projects/create/components/SelectProjectType.scss b/src/projects/create/components/SelectProjectType.scss index 86c8cc40d..dad26957e 100644 --- a/src/projects/create/components/SelectProjectType.scss +++ b/src/projects/create/components/SelectProjectType.scss @@ -15,6 +15,15 @@ .SelectProjectType { @extend .wizardCardContainer; + display: flex; + flex-direction: column; + min-height: 100vh; + background: $tc-gray-neutral-dark; + max-width: none; + width: 100%; + align-items: center; + padding-bottom: 0; + @media screen and (max-width: $screen-md - 1px) { padding-left: 2 * $base-unit; padding-right: 2 * $base-unit; @@ -22,7 +31,8 @@ .cards { margin: 0 auto; - max-width: 900px; + max-width: 100% !important; + margin-bottom: 50px; @media screen and (max-width: $screen-md - 1px) { max-width: 600px; @@ -30,18 +40,43 @@ } > h1 { - @extend .wizardHeadline; - margin-bottom: 30px; - margin-top: 30px; + @include roboto-medium; + + color: $tc-gray-90; + margin-bottom: 79px; + margin-top: 41px; + font-size: 34px; + line-height: 41px; + text-transform: uppercase; @media screen and (max-width: $screen-md - 1px) { font-size: 20px; margin-left: 10px; - margin-bottom: 0; + margin-bottom: 30px; margin-top: 10px; padding-top: 10px; text-align: left; } } + + .footer { + position: relative; + margin-top: auto; + display: flex; + flex-wrap: wrap; + + a { + display: flex; + align-items: center; + + svg { + margin-left: 7px; + + path { + fill: $tc-dark-blue-100; + } + } + } + } } } diff --git a/src/projects/create/components/SelectProjectTypeCard.jsx b/src/projects/create/components/SelectProjectTypeCard.jsx new file mode 100644 index 000000000..02745079a --- /dev/null +++ b/src/projects/create/components/SelectProjectTypeCard.jsx @@ -0,0 +1,35 @@ +import _ from 'lodash' +import React from 'react' +import PT from 'prop-types' +import './SelectProjectTypeCard.scss' + +function SelectProjectTypeCard(p) { + return ( +
+
{ p.icon }
+

{ p.type }

+
{ p.info }
+ +
+ ) +} + +SelectProjectTypeCard.defaultProps = { + disabled: false, +} + +SelectProjectTypeCard.propTypes = { + disabled: PT.bool, + icon: PT.element, + info: PT.string, + onClick: PT.func.isRequired, + type: PT.string.isRequired, + buttonText: PT.string.isRequired +} + +export default SelectProjectTypeCard diff --git a/src/projects/create/components/SelectProjectTypeCard.scss b/src/projects/create/components/SelectProjectTypeCard.scss new file mode 100644 index 000000000..2f6a391d2 --- /dev/null +++ b/src/projects/create/components/SelectProjectTypeCard.scss @@ -0,0 +1,46 @@ +@import '~tc-ui/src/styles/tc-includes'; + +.SelectProjectTypeCard { + background: $tc-white; + display: flex; + flex-direction: column; + align-items: center; + border-radius: 10px; + box-shadow: 0 7px 20px 0 rgba($tc-gray-80, 0.1); + width: 420px; + min-height: 370px; + max-width: 100%; + margin: 0 15px 30px; + padding: 35px 27px 30px 28px; + + @media screen and (max-width: $screen-md - 1px) { + margin-left: 0; + margin-right: 0; + } +} + +.icon-wrapper { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 26px; +} + +.header { + @include roboto-bold; + + color: $tc-gray-90; + font-size: 26px; + line-height: 31px; + text-transform: uppercase; +} + +.sub-type-details { + color: $tc-gray-70; + font-size: 14px; + line-height: 17px; + width: 100%; + text-align: left; + flex: 1; + margin: 19px 0; +} diff --git a/src/projects/create/components/_layout.scss b/src/projects/create/components/_layout.scss index 9c613faa2..5f23b8cb3 100644 --- a/src/projects/create/components/_layout.scss +++ b/src/projects/create/components/_layout.scss @@ -99,7 +99,9 @@ $minimumPageWidth: 984px; bottom: 0; left: 0; right: 0; - margin-bottom: 15px; + margin-bottom: 30px; + font-size: 14px; + color: $tc-gray-90; @media screen and (max-width: $screen-md - 1px) { margin-bottom: 2 * $base-unit; @@ -107,6 +109,10 @@ $minimumPageWidth: 984px; } > a { + font-size: 15px; + text-transform: uppercase; + margin-left: 5px; + @media screen and (max-width: $screen-md - 1px) { white-space: nowrap; } From cfa2b9584c4dd7f3c8ae6bd020ebb538366ef8f5 Mon Sep 17 00:00:00 2001 From: dat Date: Wed, 15 Jan 2020 11:28:03 +0700 Subject: [PATCH 09/68] ie fix ie fix --- src/helpers/templates.js | 6 +-- .../create/components/ProjectWizard.scss | 6 +++ .../components/SelectProjectTemplate.scss | 19 +++++++- .../components/SelectProjectTemplateCard.js | 43 ++++++++++-------- .../components/SelectProjectTemplateCard.scss | 44 ++++++++++++++++--- .../create/components/SelectProjectType.jsx | 3 +- .../create/components/SelectProjectType.scss | 10 +++-- .../components/SelectProjectTypeCard.jsx | 16 +++---- .../components/SelectProjectTypeCard.scss | 5 ++- 9 files changed, 106 insertions(+), 46 deletions(-) diff --git a/src/helpers/templates.js b/src/helpers/templates.js index 82974782d..b2f17a83e 100644 --- a/src/helpers/templates.js +++ b/src/helpers/templates.js @@ -140,13 +140,13 @@ export function getProjectTemplatesByCategory(projectTemplates, categoryKey, vis * Get project templates by sub category * * @param {Array} projectTemplates list of project templates - * @param {String} categoryKey category key + * @param {String} subCategoryKey sub category key * @param {Boolean} visibleOnly if true only not hidden and not disabled project templates will returned * * @returns {Array} list of project templates */ -export function getProjectTemplatesBySubCategory(projectTemplates, categoryKey, visibleOnly) { - return _.filter(projectTemplates, { subCategory: categoryKey }) +export function getProjectTemplatesBySubCategory(projectTemplates, subCategoryKey, visibleOnly) { + return _.filter(projectTemplates, { subCategory: subCategoryKey }) .filter((projectTemplate) => visibleOnly ? !projectTemplate.hidden && !projectTemplate.disabled : true) } diff --git a/src/projects/create/components/ProjectWizard.scss b/src/projects/create/components/ProjectWizard.scss index 49029805d..7cb0da559 100644 --- a/src/projects/create/components/ProjectWizard.scss +++ b/src/projects/create/components/ProjectWizard.scss @@ -13,6 +13,12 @@ align-items: flex-start; } + &.ProjectWizard-step-2 { + .content { + flex-basis: 100%; + } + } + &.ProjectWizard-step-0 { align-items: flex-start; padding-top: 30px; diff --git a/src/projects/create/components/SelectProjectTemplate.scss b/src/projects/create/components/SelectProjectTemplate.scss index 94e37cc9a..baef77bf8 100644 --- a/src/projects/create/components/SelectProjectTemplate.scss +++ b/src/projects/create/components/SelectProjectTemplate.scss @@ -25,7 +25,7 @@ align-items: center; padding-left: 30px; padding-right: 30px; - margin-bottom: 50px; + margin-bottom: 105px; max-width: 1110px; width: 100%; @@ -69,12 +69,15 @@ } .footer { - position: relative; margin-top: auto; display: flex; flex-wrap: wrap; + margin-bottom: 30px; + justify-content: center; a { + @include roboto-medium; + display: flex; align-items: center; @@ -98,6 +101,8 @@ line-height: 41px; margin-bottom: 11px; text-transform: uppercase; + padding-left: 50px; + padding-right: 50px; @media screen and (max-width: $screen-md - 1px) { font-size: 20px; @@ -188,6 +193,11 @@ svg { width: 7px; height: 7px; + flex-shrink: 0; + + @media all and (-ms-high-contrast: none), (-ms-high-contrast: active) { + margin-left: -1px; + } path { fill: $tc-gray-70; @@ -203,6 +213,11 @@ border-radius: 20px; font-size: 14px; margin: 0; + color: $tc-gray-70; + + &::-ms-clear { + display: none; + } @include placeholder { color: $tc-gray-70; diff --git a/src/projects/create/components/SelectProjectTemplateCard.js b/src/projects/create/components/SelectProjectTemplateCard.js index fdfe9e88f..3caa2ffc2 100644 --- a/src/projects/create/components/SelectProjectTemplateCard.js +++ b/src/projects/create/components/SelectProjectTemplateCard.js @@ -29,20 +29,25 @@ function SelectProjectTemplateCard(p) {

{projectTemplate.name}

{projectTemplate.info}
-
- {subTextHTML && ( -
- )} - {detailLink && ( - Learn More - )} -
+ {(subTextHTML || detailLink || deliverables.length === 0) && ( +
+ )} + {(subTextHTML || detailLink) && ( +
+ {subTextHTML && ( +
+ )} + {detailLink && ( + Learn More + )} +
+ )}
@@ -74,10 +79,12 @@ function SelectProjectTemplateCard(p) { styleName="starting-at" /> )} - + {(deliverables.length === 1) && ( + + )}
) )} diff --git a/src/projects/create/components/SelectProjectTemplateCard.scss b/src/projects/create/components/SelectProjectTemplateCard.scss index 37c8d22b4..714a95752 100644 --- a/src/projects/create/components/SelectProjectTemplateCard.scss +++ b/src/projects/create/components/SelectProjectTemplateCard.scss @@ -16,6 +16,14 @@ } .one-deliverable { + .right-card { + flex: 1; + + @media screen and (max-width: $screen-md - 1px) { + display: none; + } + } + .bottom-card { display: none; @@ -26,6 +34,10 @@ background: transparent; padding: 0; + @media screen and (max-width: $screen-sm - 1px) { + margin-bottom: 0; + } + h3 { color: $tc-gray-70; text-transform: none; @@ -43,7 +55,9 @@ .more-deliverable, .no-deliverable { .right-card { - flex: 0; + @media screen and (max-width: $screen-md - 1px) { + margin-top: 21px; + } } } @@ -79,7 +93,6 @@ width: 100%; text-align: left; margin-top: 10px; - margin-bottom: 21px; } .icon-wrapper { @@ -95,6 +108,9 @@ @media screen and (max-width: $screen-md - 1px) { flex-direction: column; text-align: center; + height: 100%; + padding-bottom: 10px; + flex-basis: 100%; .left-card-content { align-items: center; @@ -114,14 +130,17 @@ background: $tc-gray-neutral-light; align-items: center; padding: 35px 50px 30px 50px; - flex: 1; .info-html { margin-right: 22px; + margin-bottom: auto; + } + + button { + flex-shrink: 0; } @media screen and (max-width: $screen-md - 1px) { - flex: 0; background: transparent; .vertical-curve, @@ -155,6 +174,12 @@ display: flex; flex-direction: column; align-items: flex-start; + min-height: 100%; + flex-shrink: 0; + + .vertical-space { + height: 21px; + } } .bottom-card { @@ -175,6 +200,9 @@ .horizontal-curve { width: 100%; height: 30px; + position: absolute; + top: 0; + left: 0; } .learn-more-container { @@ -186,6 +214,11 @@ line-height: 17px; flex-wrap: wrap; align-items: center; + margin-top: auto; + + .starting-at { + margin-right: 10px; + } } .starting-at { @@ -258,7 +291,7 @@ .bottom-column { display: flex; flex-direction: column; - width: 100%; + flex-basis: 100%; margin: 14px; background: $tc-white; border-radius: 10px; @@ -296,4 +329,5 @@ justify-content: center; flex-wrap: wrap; padding: 16px 25px 27px 26px; + margin-top: 30px; } diff --git a/src/projects/create/components/SelectProjectType.jsx b/src/projects/create/components/SelectProjectType.jsx index 7d2627598..5beb62733 100644 --- a/src/projects/create/components/SelectProjectType.jsx +++ b/src/projects/create/components/SelectProjectType.jsx @@ -30,10 +30,9 @@ const SelectProjectType = ({ cards.push( onProjectTypeChange(projectType.key)} - type={projectType.displayName} buttonText="View Solutions" /> ) diff --git a/src/projects/create/components/SelectProjectType.scss b/src/projects/create/components/SelectProjectType.scss index dad26957e..288e1ad61 100644 --- a/src/projects/create/components/SelectProjectType.scss +++ b/src/projects/create/components/SelectProjectType.scss @@ -32,7 +32,8 @@ .cards { margin: 0 auto; max-width: 100% !important; - margin-bottom: 50px; + margin-bottom: 105px; + width: 100%; @media screen and (max-width: $screen-md - 1px) { max-width: 600px; @@ -56,16 +57,19 @@ margin-top: 10px; padding-top: 10px; text-align: left; + padding-right: 50px; } } .footer { - position: relative; - margin-top: auto; display: flex; flex-wrap: wrap; + margin-bottom: 30px; + justify-content: center; a { + @include roboto-medium; + display: flex; align-items: center; diff --git a/src/projects/create/components/SelectProjectTypeCard.jsx b/src/projects/create/components/SelectProjectTypeCard.jsx index 02745079a..ef6f84de9 100644 --- a/src/projects/create/components/SelectProjectTypeCard.jsx +++ b/src/projects/create/components/SelectProjectTypeCard.jsx @@ -1,34 +1,28 @@ -import _ from 'lodash' import React from 'react' import PT from 'prop-types' import './SelectProjectTypeCard.scss' function SelectProjectTypeCard(p) { + const { projectType } = p return (
{ p.icon }
-

{ p.type }

-
{ p.info }
+

{ projectType.displayName }

+
{ projectType.info }
) } -SelectProjectTypeCard.defaultProps = { - disabled: false, -} - SelectProjectTypeCard.propTypes = { - disabled: PT.bool, icon: PT.element, - info: PT.string, + projectType: PT.any.isRequired, onClick: PT.func.isRequired, - type: PT.string.isRequired, buttonText: PT.string.isRequired } diff --git a/src/projects/create/components/SelectProjectTypeCard.scss b/src/projects/create/components/SelectProjectTypeCard.scss index 2f6a391d2..18050c4d5 100644 --- a/src/projects/create/components/SelectProjectTypeCard.scss +++ b/src/projects/create/components/SelectProjectTypeCard.scss @@ -7,11 +7,11 @@ align-items: center; border-radius: 10px; box-shadow: 0 7px 20px 0 rgba($tc-gray-80, 0.1); - width: 420px; + max-width: 420px; min-height: 370px; - max-width: 100%; margin: 0 15px 30px; padding: 35px 27px 30px 28px; + flex-basis: 100%; @media screen and (max-width: $screen-md - 1px) { margin-left: 0; @@ -33,6 +33,7 @@ font-size: 26px; line-height: 31px; text-transform: uppercase; + width: 100%; } .sub-type-details { From 4717ea18a43abc9c7982af81c38a4edf939b59d1 Mon Sep 17 00:00:00 2001 From: dat Date: Wed, 15 Jan 2020 11:36:12 +0700 Subject: [PATCH 10/68] enter key support enter key support --- src/projects/create/components/SelectProjectTemplate.jsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/projects/create/components/SelectProjectTemplate.jsx b/src/projects/create/components/SelectProjectTemplate.jsx index be54ed220..7562ff85f 100644 --- a/src/projects/create/components/SelectProjectTemplate.jsx +++ b/src/projects/create/components/SelectProjectTemplate.jsx @@ -211,6 +211,11 @@ class SelectProjectTemplate extends React.Component{ placeholder="Search Solutions…" value={searchInputValue} onChange={(evt) => { this.setState({ searchInputValue: evt.target.value }) }} + onKeyDown={(evt) => { + if (evt.key === 'Enter') { + this.setState({ selectedSearchInputValue: searchInputValue }) + } + }} /> {(searchInputValue || selectedSearchInputValue) && ( + )} +
{pendingAttachments &&
  • Type
    -
    Name
    +
    + nameFieldRef = comp} + title="Name" + setFilter={setFilter} + filterName="name" + value={getFilterValue('name')} + /> +
    +
    + sharedWithFieldRef = comp} + title="Shared With" + filterName="sharedWith" + setFilter={setFilter} + value={getFilterValue('sharedWith')} + /> +
    Created By
    -
    Modified
    +
    + dateFieldRef = comp} + type="date" + title="Date" + setFilter={setFilter} + from={getFilterValue('date.from')} + to={getFilterValue('date.to')} + /> +
  • {links.map((link, idx) => { @@ -123,7 +249,10 @@ const FilesGridView = ({ const handleEditClick = () => onEditIntent(idx) const canEdit = `${link.createdBy}` === `${loggedInUser.userId}` - const changeSubFolder = () => onChangeSubFolder(link) + const changeSubFolder = () => { + onChangeSubFolder(link) + selectedLink = link + } const owner = _.find(assetsMembers, m => m.userId === _.parseInt(link.createdBy)) if (Array.isArray(link.children) && link.children.length > 0) { @@ -131,6 +260,9 @@ const FilesGridView = ({
  • {formatFolderTitle(link.title)}

    +
    + {renderSharedWith(link)} +
    {!owner && (
    Unknown
    )} {owner && ( @@ -170,6 +302,9 @@ const FilesGridView = ({

    {renderLink(link)}

    +
    + {renderSharedWith(link)} +
    {!owner && (
    Unknown
    )} {owner && ( @@ -215,6 +350,10 @@ FilesGridView.propTypes = { onDeletePostAttachment: PropTypes.func, formatModifyDate: PropTypes.func.isRequired, formatFolderTitle: PropTypes.func.isRequired, + setFilter: PropTypes.func.isRequired, + getFilterValue: PropTypes.func.isRequired, + clearFilter: PropTypes.func.isRequired, + filtered: PropTypes.bool } FilesGridView.defaultProps = { diff --git a/src/components/AssetsLibrary/FilterColHeader.jsx b/src/components/AssetsLibrary/FilterColHeader.jsx new file mode 100644 index 000000000..8bd34ce2e --- /dev/null +++ b/src/components/AssetsLibrary/FilterColHeader.jsx @@ -0,0 +1,133 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Dropdown from 'appirio-tech-react-components/components/Dropdown/Dropdown' +import IconCarretDownNormal from '../../assets/icons/arrow-6px-carret-down-normal.svg' +import './FilterColHeader.scss' +import FormsyForm from 'appirio-tech-react-components/components/Formsy' + +const Formsy = FormsyForm.Formsy +const TCFormFields = FormsyForm.Fields + +class FilterColHeader extends React.Component { + + constructor(props) { + super(props) + this.state = {value: '', from: '', to: ''} + this.setFilter = this.setFilter.bind(this) + this.onChange = this.onChange.bind(this) + this.onFromDateChange = this.onFromDateChange.bind(this) + this.onToDateChange = this.onToDateChange.bind(this) + } + + setFilter(newfilter) { + this.props.setFilter(this.props.filterName, newfilter) + } + + onChange(event) { + this.setState({value: event.target.value}) + + setTimeout(() => { + this.setFilter(this.state.value) + }) + } + + onFromDateChange(name, value) { + this.setState({from: value}) + + setTimeout(() => { + this.props.setFilter('date.from', this.state.from) + }) + } + + onToDateChange(name, value) { + this.setState({to: value}) + + setTimeout(() => { + this.props.setFilter('date.to', this.state.to) + }) + } + + componentDidMount() { + this.setState({ + value: this.props.value || '', + from: this.props.from || '', + to: this.props.to || '' + }) + } + + clearFilter() { + this.setState({ + value: '', + from: '', + to: '' + }) + } + + renderByType() { + switch (this.props.type) { + case 'date': { + return ( + + + + + ) + } + + default: { + return ( + + ) + } + } + } + + render() { + const { + title, + } = this.props + + return ( +
    + + + {title} + + +
    + {this.renderByType()} +
    +
    +
    + ) + } +} + +FilterColHeader.propTypes = { + title: PropTypes.string, + filterName: PropTypes.string, + setFilter: PropTypes.func, + value: PropTypes.string, + type: PropTypes.string, + from: PropTypes.string, + to: PropTypes.string +} +export default FilterColHeader diff --git a/src/components/AssetsLibrary/FilterColHeader.scss b/src/components/AssetsLibrary/FilterColHeader.scss new file mode 100644 index 000000000..b7c6f3829 --- /dev/null +++ b/src/components/AssetsLibrary/FilterColHeader.scss @@ -0,0 +1,29 @@ +@import '~tc-ui/src/styles/tc-includes'; + +.FilterColHeader { + :global { + .dropdown-menu-list.down-layer { + padding-top: 40px; + } + + .filter-drop-down { + .txt-link { + z-index: 10; + + .icon-carret-down-normal { + top: 50%; + margin-top: -3px; + } + } + + .Dropdown.no-autoclose { + z-index: 5; + } + } + + .dropdown-wrap { + cursor: default; + } + } +} + diff --git a/src/components/AssetsLibrary/GridView.scss b/src/components/AssetsLibrary/GridView.scss index 638539628..492840501 100644 --- a/src/components/AssetsLibrary/GridView.scss +++ b/src/components/AssetsLibrary/GridView.scss @@ -8,6 +8,10 @@ line-height: 20px; text-align: left; margin-bottom: 16px; + + button { + margin-left: 10px; + } } .assets-gridview-header, @@ -43,10 +47,12 @@ font-weight: 400; line-height: 20px; text-align: left; + word-break: break-all; } .flex-item-title { color: $tc-gray-50; + font-size: $tc-body-sm; } .flex-item, @@ -72,7 +78,6 @@ -ms-flex: 8 1 505px; flex: 8 1 505px; font-family: Roboto; - font-size: 15px; font-weight: 400; line-height: 30px; min-width: 0; @@ -85,15 +90,18 @@ } } +.item-shared-with { + flex: none; + width: 140px; +} + .item-modified, .item-created-by { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 140px; - text-align: right; display: inline-block; font-family: Roboto; - font-size: 12px; font-weight: 400; line-height: 30px; } @@ -102,7 +110,7 @@ -webkit-box-flex: 0; -ms-flex: none; flex: none; - width: 100px; + width: 50px; justify-content: flex-end; padding-right: 4px; } @@ -141,4 +149,14 @@ text-align: left; line-height: 20px; } -} \ No newline at end of file + + .user-block { + .project-customer { + position: relative; + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } +} diff --git a/src/components/AssetsLibrary/LinksGridView.jsx b/src/components/AssetsLibrary/LinksGridView.jsx index b73c5b9ce..82b21d660 100644 --- a/src/components/AssetsLibrary/LinksGridView.jsx +++ b/src/components/AssetsLibrary/LinksGridView.jsx @@ -2,7 +2,8 @@ import React from 'react' import PropTypes from 'prop-types' import uncontrollable from 'uncontrollable' import cn from 'classnames' -import { Link } from 'react-router-dom' +import { Link } from 'react-router-dom' +import _ from 'lodash' import DeleteLinkModal from '../LinksMenu/DeleteLinkModal' import EditLinkModal from '../LinksMenu/EditLinkModal' @@ -14,6 +15,16 @@ import FolderIcon from '../../assets/icons/v.2.5/icon-folder-small.svg' import LinkIcon from '../../assets/icons/link-12.svg' import './GridView.scss' +import { + PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS, + PROJECT_ASSETS_SHARED_WITH_TOPCODER_MEMBERS, + PROJECT_FEED_TYPE_MESSAGES +} from '../../config/constants' +import FilterColHeader from './FilterColHeader' + +let selectedLink +let clearing = false + const LinksGridView = ({ canDelete, canEdit, @@ -30,7 +41,40 @@ const LinksGridView = ({ formatModifyDate, formatFolderTitle, assetsMembers, + setFilter, + getFilterValue, + clearFilter, + filtered }) => { + let nameFieldRef + let sharedWithFieldRef + let dateFieldRef + + const updateSubContents = () => { + if (selectedLink) { + let link = links.filter(item => { + return selectedLink.title === item.title + && selectedLink.createdBy === item.createdBy + && selectedLink.updatedAt === item.updatedAt + })[0] + + if (!link) { + link = _.cloneDeep(selectedLink) + link.children = [] + } + + onChangeSubFolder(link) + } + } + + const clearSubContents = () => clearing = true + + const clearFieldValues = () => { + nameFieldRef.clearFilter() + sharedWithFieldRef.clearFilter() + dateFieldRef.clearFilter() + } + const renderLink = (link) => { if (link.onClick) { return ( @@ -52,7 +96,23 @@ const LinksGridView = ({ return {link.title} } } - const goBack = () => onChangeSubFolder(null) + const goBack = () => { + onChangeSubFolder(null) + selectedLink = null + } + + const getSharedWithText = (tag) => { + return tag === PROJECT_FEED_TYPE_MESSAGES + ? PROJECT_ASSETS_SHARED_WITH_TOPCODER_MEMBERS : PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS + } + + if (clearing) { + setTimeout(() => { + updateSubContents() + clearing = false + }) + } + return (
    {(subFolderContent) && ( @@ -63,17 +123,62 @@ const LinksGridView = ({ assetsMembers={assetsMembers} isLinkSubFolder formatModifyDate={formatModifyDate} + setFilter={setFilter} + getFilterValue={getFilterValue} + clearFilter={clearFilter} + updateSubContents={updateSubContents} + clearSubContents={clearSubContents} + filtered={filtered} />)} {(!subFolderContent) && (
    = 0 || linkToDelete >= 0)}, '')}> {(linkToEdit >= 0 || linkToDelete >= 0) &&
    } -
    {`All ${title}`}
    +
    + {`${filtered ? 'Filtered' : 'All'} ${title}`} + {filtered && ( + + )} +