From 16fa1d19cb42f0d7bedde9d4809fed1e24c179c8 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 1 Jun 2020 19:52:01 +0530 Subject: [PATCH 01/32] fix: Github issue#666, Standard Design "Edit" Event Sequence Out of Order --- src/services/challenges.js | 3 ++- src/util/date.js | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/services/challenges.js b/src/services/challenges.js index 9fa6150c..f845daed 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -1,7 +1,7 @@ import _ from 'lodash' import qs from 'qs' import { axiosInstance } from './axiosWithAuth' -import { updateChallengePhaseBeforeSendRequest, convertChallengePhaseFromSecondsToHours } from '../util/date' +import { updateChallengePhaseBeforeSendRequest, convertChallengePhaseFromSecondsToHours, sortChallengePhases } from '../util/date' import FormData from 'form-data' const { CHALLENGE_API_URL, @@ -88,6 +88,7 @@ export async function fetchChallenge (challengeId) { } } convertChallengePhaseFromSecondsToHours(newResponse.phases) + newResponse.phases = sortChallengePhases(newResponse.phases) return newResponse } diff --git a/src/util/date.js b/src/util/date.js index 5dcf3ad0..84618569 100644 --- a/src/util/date.js +++ b/src/util/date.js @@ -56,6 +56,15 @@ export const convertChallengePhaseFromSecondsToHours = (phases) => { } } +/** + * Sorts the challenge phases in order of their supposed execution + * + * @param {Array} phases challenge phases that are to be sorte3d + */ +export const sortChallengePhases = (phases) => { + return _.sortBy(phases, phase => phase.actualStartDate || phase.scheduledStartDate) +} + /** * Convert challenge phase from hours to second and remove unnessesary field * @param {Object} challengeDetail challenge detail From 6ed5a2c454a994a99f977be09787e95fc31aea87 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 3 Jun 2020 09:14:29 +0530 Subject: [PATCH 02/32] feat: git#669 project search improvements - allows search by id and exact phrase (to enable searching projects by title) feat: git#669 project search improvements - moved the project search from left panel to main content area to have sufficient space to locate the project feat: git#669 project search improvements - allows searching non my projects as well to support admin users (e.g. support team) to locate other projects/challenges fix: minor style fix for the left panel --- package-lock.json | 120 ++++++----------- public/index.html | 2 +- src/actions/sidebar.js | 16 ++- src/components/Sidebar/Sidebar.module.scss | 31 +---- src/components/Sidebar/index.js | 47 +------ .../Challenges/Challenges.module.scss | 51 +++++++ src/containers/Challenges/index.js | 125 ++++++++++++++---- src/containers/Sidebar/index.js | 2 +- src/containers/TopbarContainer/index.js | 15 ++- src/reducers/sidebar.js | 2 +- src/routes.js | 2 +- 11 files changed, 224 insertions(+), 189 deletions(-) create mode 100644 src/containers/Challenges/Challenges.module.scss diff --git a/package-lock.json b/package-lock.json index 46facb70..5c6ddc0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3220,14 +3220,12 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "optional": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-glob": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "optional": true, "requires": { "is-extglob": "^2.1.1" } @@ -3241,8 +3239,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "to-regex-range": { "version": "5.0.1", @@ -6148,8 +6145,7 @@ }, "ansi-regex": { "version": "2.1.1", - "bundled": true, - "optional": true + "bundled": true }, "aproba": { "version": "1.2.0", @@ -6167,13 +6163,11 @@ }, "balanced-match": { "version": "1.0.0", - "bundled": true, - "optional": true + "bundled": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6186,18 +6180,15 @@ }, "code-point-at": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "concat-map": { "version": "0.0.1", - "bundled": true, - "optional": true + "bundled": true }, "console-control-strings": { "version": "1.1.0", - "bundled": true, - "optional": true + "bundled": true }, "core-util-is": { "version": "1.0.2", @@ -6300,8 +6291,7 @@ }, "inherits": { "version": "2.0.4", - "bundled": true, - "optional": true + "bundled": true }, "ini": { "version": "1.3.5", @@ -6311,7 +6301,6 @@ "is-fullwidth-code-point": { "version": "1.0.0", "bundled": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6324,20 +6313,17 @@ "minimatch": { "version": "3.0.4", "bundled": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "1.2.5", - "bundled": true, - "optional": true + "bundled": true }, "minipass": { "version": "2.9.0", "bundled": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6354,7 +6340,6 @@ "mkdirp": { "version": "0.5.3", "bundled": true, - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -6410,8 +6395,7 @@ }, "npm-normalize-package-bin": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "npm-packlist": { "version": "1.4.8", @@ -6436,8 +6420,7 @@ }, "number-is-nan": { "version": "1.0.1", - "bundled": true, - "optional": true + "bundled": true }, "object-assign": { "version": "4.1.1", @@ -6447,7 +6430,6 @@ "once": { "version": "1.4.0", "bundled": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6516,8 +6498,7 @@ }, "safe-buffer": { "version": "5.1.2", - "bundled": true, - "optional": true + "bundled": true }, "safer-buffer": { "version": "2.1.2", @@ -6547,7 +6528,6 @@ "string-width": { "version": "1.0.2", "bundled": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6565,7 +6545,6 @@ "strip-ansi": { "version": "3.0.1", "bundled": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6604,13 +6583,11 @@ }, "wrappy": { "version": "1.0.2", - "bundled": true, - "optional": true + "bundled": true }, "yallist": { "version": "3.1.1", - "bundled": true, - "optional": true + "bundled": true } } }, @@ -10411,8 +10388,7 @@ "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "optional": true + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" }, "pify": { "version": "2.3.0", @@ -17118,9 +17094,9 @@ "angular-cookies": "^1.5.1", "angular-material": "^1.1.21", "angular-messages": "^1.5.2", - "appirio-tech-ng-iso-constants": "github:appirio-tech/ng-iso-constants#v1.0.7", + "appirio-tech-ng-iso-constants": "github:appirio-tech/ng-iso-constants#d8466ab76828208ccdaaeb10816a3f35cd59c39b", "appirio-tech-ng-ui-components": "^2.2.4", - "appirio-tech-react-components": "github:appirio-tech/react-components#feature/connectv2", + "appirio-tech-react-components": "github:appirio-tech/react-components#a471d4f9d1a4cd5a1a2f53aea3d1cc5dd6d78aea", "auth0-js": "^9.6.1", "babel-polyfill": "^6.7.4", "filestack-js": "^1.13.2", @@ -17206,7 +17182,8 @@ }, "acorn": { "version": "5.7.1", - "resolved": "" + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.1.tgz", + "integrity": "sha512-d+nbxBUGKg7Arpsvbnlq61mc12ek3EY8EQldM3GPAhWJ1UVxC6TDGbIvUMNU6obBX3i1+ptCIzV4vq0gFPEGVQ==" }, "acorn-globals": { "version": "1.0.9", @@ -17646,7 +17623,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": { @@ -20685,7 +20662,8 @@ }, "extend": { "version": "3.0.1", - "resolved": "" + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "extglob": { "version": "0.3.2", @@ -21071,8 +21049,7 @@ "ansi-regex": { "version": "2.1.1", "resolved": false, - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "optional": true + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "aproba": { "version": "1.2.0", @@ -21093,14 +21070,12 @@ "balanced-match": { "version": "1.0.0", "resolved": false, - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "optional": true + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, "brace-expansion": { "version": "1.1.11", "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -21115,20 +21090,17 @@ "code-point-at": { "version": "1.1.0", "resolved": false, - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "optional": true + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "concat-map": { "version": "0.0.1", "resolved": false, - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "optional": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", "resolved": false, - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "optional": true + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-util-is": { "version": "1.0.2", @@ -21245,8 +21217,7 @@ "inherits": { "version": "2.0.3", "resolved": false, - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "optional": true + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "ini": { "version": "1.3.5", @@ -21258,7 +21229,6 @@ "version": "1.0.0", "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -21273,7 +21243,6 @@ "version": "3.0.4", "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -21281,14 +21250,12 @@ "minimist": { "version": "0.0.8", "resolved": false, - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "optional": true + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "minipass": { "version": "2.3.5", "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -21307,7 +21274,6 @@ "version": "0.5.1", "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "optional": true, "requires": { "minimist": "0.0.8" } @@ -21394,8 +21360,7 @@ "number-is-nan": { "version": "1.0.1", "resolved": false, - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "optional": true + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, "object-assign": { "version": "4.1.1", @@ -21407,7 +21372,6 @@ "version": "1.4.0", "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "optional": true, "requires": { "wrappy": "1" } @@ -21493,8 +21457,7 @@ "safe-buffer": { "version": "5.1.2", "resolved": false, - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "optional": true + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "safer-buffer": { "version": "2.1.2", @@ -21530,7 +21493,6 @@ "version": "1.0.2", "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -21550,7 +21512,6 @@ "version": "3.0.1", "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -21594,14 +21555,12 @@ "wrappy": { "version": "1.0.2", "resolved": false, - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "optional": true + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "yallist": { "version": "3.0.3", "resolved": false, - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", - "optional": true + "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" } } }, @@ -23154,7 +23113,8 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "requires": { "minimist": "0.0.8" } @@ -27904,8 +27864,7 @@ "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "optional": true + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" }, "binary-extensions": { "version": "1.13.1", @@ -27917,7 +27876,6 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "optional": true, "requires": { "arr-flatten": "^1.1.0", "array-unique": "^0.3.2", @@ -28047,7 +28005,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, "requires": { "is-extendable": "^0.1.0" } @@ -28083,7 +28040,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "optional": true, "requires": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", @@ -28177,8 +28133,7 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "optional": true + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-glob": { "version": "4.0.1", @@ -28193,7 +28148,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "optional": true, "requires": { "kind-of": "^3.0.2" } diff --git a/public/index.html b/public/index.html index e1058e7d..5954f369 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - Topcoder Challenge Creation App + Work Manager - Topcoder diff --git a/src/actions/sidebar.js b/src/actions/sidebar.js index 04dd84a8..1abd3c90 100644 --- a/src/actions/sidebar.js +++ b/src/actions/sidebar.js @@ -21,7 +21,7 @@ export function setActiveProject (projectId) { /** * Loads projects of the authenticated user */ -export function loadProjects (filterProjectName = '') { +export function loadProjects (filterProjectName = '', myProjects = true) { return (dispatch) => { dispatch({ type: LOAD_PROJECTS_PENDING @@ -29,11 +29,19 @@ export function loadProjects (filterProjectName = '') { const filters = {} if (!_.isEmpty(filterProjectName)) { - filters['name'] = `*${filterProjectName}*` + if (!isNaN(filterProjectName)) { // if it is number + filters['id'] = parseInt(filterProjectName, 10) + } else { // text search + filters['keyword'] = decodeURIComponent(filterProjectName) + } } filters['status'] = 'active' - filters['sort'] = 'lastActivityAt' - filters['memberOnly'] = 'true' + filters['sort'] = 'lastActivityAt desc' + // filters['perPage'] = 20 + // filters['page'] = 1 + if (myProjects) { + filters['memberOnly'] = true + } fetchMemberProjects(filters).then(projects => dispatch({ type: LOAD_PROJECTS_SUCCESS, diff --git a/src/components/Sidebar/Sidebar.module.scss b/src/components/Sidebar/Sidebar.module.scss index 3c36e57a..8b87d068 100644 --- a/src/components/Sidebar/Sidebar.module.scss +++ b/src/components/Sidebar/Sidebar.module.scss @@ -5,34 +5,11 @@ height: 100vh; background-color: $dark-gray; overflow: auto; - - ul { - list-style: none; - width: 100%; - padding: 0; - margin-top: 0; - - li { - cursor: pointer; - } - } - - a { - text-decoration: none; - } - - .noProjects { - @include roboto; - font-weight: 400; - padding-left: 30px; - padding-right: 20px; - color: $white; - } } .logo { width: calc(100% - 100px); - padding: 30px 50px 42px 50px; + padding: 30px 50px 42px 30px; @media screen and (max-width: 300px * 4) { width: calc(100% - 20px); padding: 30px 10px 42px 10px; @@ -46,10 +23,8 @@ font-weight: 400; line-height: 31px; color: $white; - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 37px + margin-bottom: 37px; + padding-left: 30px; } .homeLink { diff --git a/src/components/Sidebar/index.js b/src/components/Sidebar/index.js index 97bc0c76..9869e77f 100644 --- a/src/components/Sidebar/index.js +++ b/src/components/Sidebar/index.js @@ -5,71 +5,28 @@ import React from 'react' import { Link } from 'react-router-dom' import PropTypes from 'prop-types' import cn from 'classnames' -import _ from 'lodash' -import ProjectCard from '../ProjectCard' -import { DebounceInput } from 'react-debounce-input' -import Loader from '../Loader' import TopcoderLogo from '../../assets/images/topcoder-logo.png' import styles from './Sidebar.module.scss' const Sidebar = ({ - projects, isLoading, setActiveProject, - projectId, resetSidebarActiveParams, - updateProjectsList, searchProjectName + projectId, resetSidebarActiveParams }) => { - const projectComponents = projects.map(p => ( -
  • - -
  • - )) - return (
    Challenge Editor
    - { - !isLoading && !searchProjectName && _.get(projectComponents, 'length', 0) === 0 && ( -
    - You don't have any active projects yet! -
    - ) - }
    Active challenges
    - updateProjectsList(e.target.value)} - value={searchProjectName} - /> - { - isLoading ? : ( -
      - {projectComponents} -
    - ) - }
    ) } Sidebar.propTypes = { - projects: PropTypes.arrayOf(PropTypes.shape()), - isLoading: PropTypes.bool, - setActiveProject: PropTypes.func, projectId: PropTypes.string, - resetSidebarActiveParams: PropTypes.func, - searchProjectName: PropTypes.string, - updateProjectsList: PropTypes.func + resetSidebarActiveParams: PropTypes.func } export default Sidebar diff --git a/src/containers/Challenges/Challenges.module.scss b/src/containers/Challenges/Challenges.module.scss new file mode 100644 index 00000000..01cafd76 --- /dev/null +++ b/src/containers/Challenges/Challenges.module.scss @@ -0,0 +1,51 @@ +@import "../../styles/includes"; + +.projectSearch { + padding: 20px 20px 0px; + + .projectSearchHeader { + display: flex; + + label { + line-height: 40px; + margin-right: 10px; + } + } + + ul { + background-color: $lighter-gray; + list-style: none; + width: 100%; + padding: 0; + margin-top: 0; + border-radius: 3px; + flex: 1; + + li { + cursor: pointer; + } + } + + input[type="text"] { + flex: 2; + } + + input[type="checkbox"] { + margin-left: 20px; + margin-right: 10px; + height: 40px; + } + + a { + color: $dark-gray; + text-decoration: none; + } + + .noProjects { + @include roboto; + font-weight: 400; + padding-left: 30px; + padding-right: 20px; + color: $white; + } +} \ No newline at end of file diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index f51dde62..6525e4b2 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -2,21 +2,39 @@ * Container to render Challenges page */ import _ from 'lodash' -import React, { Component } from 'react' +import React, { Component, Fragment } from 'react' // import { Redirect } from 'react-router-dom' import PropTypes from 'prop-types' import { connect } from 'react-redux' +import { DebounceInput } from 'react-debounce-input' import ChallengesComponent from '../../components/ChallengesComponent' +import ProjectCard from '../../components/ProjectCard' +import Loader from '../../components/Loader' import { loadChallengesByPage } from '../../actions/challenges' import { loadProject } from '../../actions/projects' -import { resetSidebarActiveParams } from '../../actions/sidebar' +import { loadProjects, setActiveProject, resetSidebarActiveParams } from '../../actions/sidebar' import { CHALLENGE_STATUS } from '../../config/constants' +import styles from './Challenges.module.scss' class Challenges extends Component { + constructor (props) { + super(props) + this.state = { + searchProjectName: '', + onlyMyProjects: true + } + + this.updateProjectName = this.updateProjectName.bind(this) + this.toggleMyProjects = this.toggleMyProjects.bind(this) + } + componentDidMount () { - const { activeProjectId, resetSidebarActiveParams, menu, projectId } = this.props + const { activeProjectId, resetSidebarActiveParams, menu, projectId, isLoading } = this.props + if (activeProjectId === -1 && !isLoading) { + this.props.loadProjects() + } if (menu === 'NULL' && activeProjectId !== -1) { resetSidebarActiveParams() } else { @@ -28,7 +46,9 @@ class Challenges extends Component { } componentWillReceiveProps (nextProps) { - this.reloadChallenges(nextProps) + if (this.props.activeProjectId !== nextProps.activeProjectId) { + this.reloadChallenges(nextProps) + } } reloadChallenges (props) { @@ -43,6 +63,17 @@ class Challenges extends Component { } } + updateProjectName (val) { + this.setState({ searchProjectName: val }) + this.props.loadProjects(val, this.state.onlyMyProjects) + } + + toggleMyProjects (evt) { + this.setState({ onlyMyProjects: evt.target.checked }, () => { + this.props.loadProjects(this.state.searchProjectName, this.state.onlyMyProjects) + }) + } + render () { const { challenges, @@ -56,26 +87,70 @@ class Challenges extends Component { loadChallengesByPage, page, perPage, - totalChallenges + totalChallenges, + setActiveProject } = this.props + const { searchProjectName, onlyMyProjects } = this.state const projectInfo = _.find(projects, { id: activeProjectId }) || {} + const projectComponents = projects.map(p => ( +
  • + +
  • + )) return ( - + +
    +
    + + this.updateProjectName(e.target.value)} + value={searchProjectName} + /> + + +
    + { + activeProjectId === -1 &&
    No project selected. Select one below
    + } + { + isLoading ? : ( +
      + {projectComponents} +
    + ) + } +
    + { activeProjectId !== -1 && + } +
    ) } } @@ -96,7 +171,9 @@ Challenges.propTypes = { resetSidebarActiveParams: PropTypes.func, page: PropTypes.number.isRequired, perPage: PropTypes.number.isRequired, - totalChallenges: PropTypes.number.isRequired + totalChallenges: PropTypes.number.isRequired, + loadProjects: PropTypes.func.isRequired, + setActiveProject: PropTypes.func.isRequired } const mapStateToProps = ({ challenges, sidebar, projects }) => ({ @@ -111,7 +188,9 @@ const mapStateToProps = ({ challenges, sidebar, projects }) => ({ const mapDispatchToProps = { loadChallengesByPage, resetSidebarActiveParams, - loadProject + loadProject, + loadProjects, + setActiveProject } export default connect(mapStateToProps, mapDispatchToProps)(Challenges) diff --git a/src/containers/Sidebar/index.js b/src/containers/Sidebar/index.js index b8764a94..ea8fa1ef 100644 --- a/src/containers/Sidebar/index.js +++ b/src/containers/Sidebar/index.js @@ -15,7 +15,7 @@ class SidebarContainer extends Component { } componentDidMount () { - this.props.loadProjects() + // this.props.loadProjects() const { projectId, activeProjectId } = this.props diff --git a/src/containers/TopbarContainer/index.js b/src/containers/TopbarContainer/index.js index d56bb7a5..3533e475 100644 --- a/src/containers/TopbarContainer/index.js +++ b/src/containers/TopbarContainer/index.js @@ -4,12 +4,19 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { loadUser } from '../../actions/auth' +import { setActiveProject } from '../../actions/sidebar' import { connect } from 'react-redux' import TopBar from '../../components/TopBar' class TopbarContainer extends Component { componentDidMount () { this.props.loadUser() + + const { projectId, activeProjectId } = this.props + + if (projectId && activeProjectId < 0) { + this.props.setActiveProject(parseInt(projectId)) + } } render () { @@ -20,7 +27,10 @@ class TopbarContainer extends Component { TopbarContainer.propTypes = { loadUser: PropTypes.func.isRequired, - auth: PropTypes.object.isRequired + setActiveProject: PropTypes.func.isRequired, + auth: PropTypes.object.isRequired, + activeProjectId: PropTypes.number, + projectId: PropTypes.string } const mapStateToProps = ({ auth }) => ({ @@ -28,7 +38,8 @@ const mapStateToProps = ({ auth }) => ({ }) const mapDispatchToProps = { - loadUser + loadUser, + setActiveProject } export default connect(mapStateToProps, mapDispatchToProps)(TopbarContainer) diff --git a/src/reducers/sidebar.js b/src/reducers/sidebar.js index 5e775474..3881840d 100644 --- a/src/reducers/sidebar.js +++ b/src/reducers/sidebar.js @@ -18,7 +18,7 @@ const initialState = { export default function (state = initialState, action) { switch (action.type) { case SET_ACTIVE_PROJECT: - return { ...state, activeProjectId: action.projectId } + return { ...state, activeProjectId: action.projectId, projects: [], isLoading: false } case LOAD_PROJECTS_SUCCESS: return { ...state, projects: action.projects, isLoading: false, isLoggedIn: true } case LOAD_PROJECTS_PENDING: diff --git a/src/routes.js b/src/routes.js index 72de1741..6e83c648 100644 --- a/src/routes.js +++ b/src/routes.js @@ -82,7 +82,7 @@ class Routes extends React.Component { renderApp( , - , + , )()} /> {/* If path is not defined redirect to landing page */} From 4318628de607c89c706a18ee5e7961bb24b10017 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 3 Jun 2020 09:19:50 +0530 Subject: [PATCH 03/32] fix: typo --- src/containers/Challenges/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index 6525e4b2..4bd3d69e 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -106,7 +106,7 @@ class Challenges extends Component {
    - + Date: Wed, 3 Jun 2020 18:30:15 +0530 Subject: [PATCH 04/32] fix: https://github.com/topcoder-platform/challenge-engine-ui/issues/597 Skipped the resource filtering for the challenge listing for now feat: added a Back button for easy access to the challenge listing page from the challenge view page --- src/actions/challenges.js | 4 ++-- .../ChallengeEditor/ChallengeView/ChallengeView.module.scss | 4 ++++ src/components/ChallengeEditor/ChallengeView/index.js | 3 ++- src/components/ChallengesComponent/ChallengeList/index.js | 6 +++++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 0ddae369..d29ee175 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -70,8 +70,8 @@ export function loadChallengesByPage (page, projectId, status, filterChallengeNa return fetchChallenges(filters, { page, - perPage: PAGE_SIZE, - memberId: getState().auth.user ? getState().auth.user.userId : null + perPage: PAGE_SIZE + // memberId: getState().auth.user ? getState().auth.user.userId : null }).then((res) => { dispatch({ type: LOAD_CHALLENGES_SUCCESS, diff --git a/src/components/ChallengeEditor/ChallengeView/ChallengeView.module.scss b/src/components/ChallengeEditor/ChallengeView/ChallengeView.module.scss index 8b609d15..e2cac20d 100644 --- a/src/components/ChallengeEditor/ChallengeView/ChallengeView.module.scss +++ b/src/components/ChallengeEditor/ChallengeView/ChallengeView.module.scss @@ -232,6 +232,10 @@ } } +.actionButtons { + display: flex; +} + .button { height: 40px; diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 26ede324..26dfdd86 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -55,8 +55,9 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources,
    View Details
    -
    +
    +
    diff --git a/src/components/ChallengesComponent/ChallengeList/index.js b/src/components/ChallengesComponent/ChallengeList/index.js index faa1d5d0..b1c2e30f 100644 --- a/src/components/ChallengesComponent/ChallengeList/index.js +++ b/src/components/ChallengesComponent/ChallengeList/index.js @@ -203,7 +203,11 @@ class ChallengeList extends Component {
      { map(challenges, (c) => { - return
    • + return ( +
    • + +
    • + ) }) }
    From 3394fb695be91f231c79292224fd591e02574c67 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Thu, 4 Jun 2020 11:58:42 +0530 Subject: [PATCH 05/32] chore: adapted for changes in payment types --- .../ChallengeEditor/ChallengePrizes-Field/index.js | 6 +++--- .../ChallengeEditor/CheckpointPrizes-Field/index.js | 4 ++-- .../ChallengeEditor/CopilotFee-Field/index.js | 4 ++-- .../ChallengeEditor/ReviewCost-Field/index.js | 4 ++-- src/components/ChallengeEditor/index.js | 6 +++--- src/config/constants.js | 12 ++++++------ 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengePrizes-Field/index.js b/src/components/ChallengeEditor/ChallengePrizes-Field/index.js index d9149888..ed33c0cd 100644 --- a/src/components/ChallengeEditor/ChallengePrizes-Field/index.js +++ b/src/components/ChallengeEditor/ChallengePrizes-Field/index.js @@ -8,7 +8,7 @@ import PrizeInput from '../../PrizeInput' import styles from './ChallengePrizes-Field.module.scss' import cn from 'classnames' import { PrimaryButton } from '../../Buttons' -import { CHALLENGE_PRIZE_TYPE, VALIDATION_VALUE_TYPE } from '../../../config/constants' +import { CHALLENGE_PRIZE_TYPE, VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' import { validateValue } from '../../../util/input-check' class ChallengePrizesField extends Component { @@ -46,14 +46,14 @@ class ChallengePrizesField extends Component { } onUpdateValue (challengePrize) { - const type = 'Challenge prizes' + const type = PRIZE_SETS_TYPE.CHALLENGE_PRIZES const { onUpdateOthers, challenge } = this.props onUpdateOthers({ field: 'prizeSets', value: [...challenge.prizeSets.filter(p => p.type !== type), challengePrize] }) } getChallengePrize () { - const type = 'Challenge prizes' + const type = PRIZE_SETS_TYPE.CHALLENGE_PRIZES return (this.props.challenge.prizeSets && this.props.challenge.prizeSets.length && this.props.challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type: CHALLENGE_PRIZE_TYPE.MONEY, value: 0 }] } } diff --git a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js index 86032aeb..9afd7790 100644 --- a/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js +++ b/src/components/ChallengeEditor/CheckpointPrizes-Field/index.js @@ -4,10 +4,10 @@ import styles from './CheckpointPrizes-Field.module.scss' import cn from 'classnames' import { range } from 'lodash' import { validateValue } from '../../../util/input-check' -import { VALIDATION_VALUE_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' const CheckpointPrizesField = ({ challenge, onUpdateOthers }) => { - const type = 'Checkpoint prizes' + const type = PRIZE_SETS_TYPE.CHECKPOINT_PRIZES const checkpointPrize = challenge.prizeSets.find(p => p.type === type) || { type, prizes: [] } const number = checkpointPrize.prizes.length const amount = checkpointPrize.prizes.length ? checkpointPrize.prizes[0].value : 0 diff --git a/src/components/ChallengeEditor/CopilotFee-Field/index.js b/src/components/ChallengeEditor/CopilotFee-Field/index.js index 734eaf8b..d7d357de 100644 --- a/src/components/ChallengeEditor/CopilotFee-Field/index.js +++ b/src/components/ChallengeEditor/CopilotFee-Field/index.js @@ -5,10 +5,10 @@ import PropTypes from 'prop-types' import { validateValue } from '../../../util/input-check' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faDollarSign } from '@fortawesome/free-solid-svg-icons' -import { VALIDATION_VALUE_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' const CopilotFeeField = ({ challenge, onUpdateOthers, readOnly }) => { - const type = 'Copilot payment' + const type = PRIZE_SETS_TYPE.COPILOT_PAYMENT const copilotFee = (challenge.prizeSets && challenge.prizeSets.find(p => p.type === type)) || { type, prizes: [{ type, value: 0 }] } const value = copilotFee.prizes[0].value diff --git a/src/components/ChallengeEditor/ReviewCost-Field/index.js b/src/components/ChallengeEditor/ReviewCost-Field/index.js index d4b9f5a6..11f288fc 100644 --- a/src/components/ChallengeEditor/ReviewCost-Field/index.js +++ b/src/components/ChallengeEditor/ReviewCost-Field/index.js @@ -3,10 +3,10 @@ import PropTypes from 'prop-types' import styles from './ReviewCost-Field.module.scss' import cn from 'classnames' import { validateValue } from '../../../util/input-check' -import { VALIDATION_VALUE_TYPE } from '../../../config/constants' +import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE } from '../../../config/constants' const ReviewCostField = ({ challenge, onUpdateOthers }) => { - const type = 'Reviewer payment' + const type = PRIZE_SETS_TYPE.REVIEWER_PAYMENT const reviewCost = challenge.prizeSets.find(p => p.type === type) || { type, prizes: [{ type, value: 0 }] } const value = reviewCost.prizes[0].value diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 8aed97d6..fada8963 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -277,7 +277,7 @@ class ChallengeEditor extends Component { value = null } if (field === 'prizeSets') { - value = value.filter(val => PRIZE_SETS_TYPE.includes(val.type)) + value = value.filter(val => _.values(PRIZE_SETS_TYPE).includes(val.type)) } newChallenge[field] = value this.setState({ challenge: newChallenge }) @@ -467,7 +467,7 @@ class ChallengeEditor extends Component { } isValidChallengePrizes () { - const challengePrizes = this.state.challenge.prizeSets.find(p => p.type === 'Challenge prizes') + const challengePrizes = this.state.challenge.prizeSets.find(p => p.type === PRIZE_SETS_TYPE.CHALLENGE_PRIZES) if (challengePrizes.prizes.length === 0) { return false } @@ -657,7 +657,7 @@ class ChallengeEditor extends Component { getDefaultPrizeSets () { return [ { - type: 'Challenge prizes', + type: PRIZE_SETS_TYPE.CHALLENGE_PRIZES, prizes: [{ type: 'money', value: '0' }] } ] diff --git a/src/config/constants.js b/src/config/constants.js index e587c2f8..6b6ceae4 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -71,12 +71,12 @@ export const PHASE_STATUS = { } // List of prize sets types -export const PRIZE_SETS_TYPE = [ - 'Challenge prizes', - 'Copilot payment', - 'Reviewer payment', - 'Checkpoint prizes' -] +export const PRIZE_SETS_TYPE = { + CHALLENGE_PRIZES: 'placement', + COPILOT_PAYMENT: 'copilot', + REVIEWER_PAYMENT: 'reviewer', + CHECKPOINT_PRIZES: 'checkpoint' +} // List of subtracks that should be considered as Marathon Matches export const MARATHON_MATCH_SUBTRACKS = [ From 38c6aacd349f3f5be2c42c597d4e6ad019c6ee0f Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Jun 2020 14:55:24 +0530 Subject: [PATCH 06/32] fix: git#679 Adapt for terms data model change Updated payload to have terms as array of objects with roleId being id of the Submitter role --- config/constants/development.js | 3 ++- config/constants/production.js | 3 ++- .../ChallengeEditor/NDAField/index.js | 3 ++- src/components/ChallengeEditor/index.js | 20 ++++++++++++------- src/config/constants.js | 3 ++- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/config/constants/development.js b/config/constants/development.js index 15478e88..31f3adae 100644 --- a/config/constants/development.js +++ b/config/constants/development.js @@ -23,5 +23,6 @@ module.exports = { DIRECT_PROJECT_URL: `https://www.${DOMAIN}/direct`, ONLINE_REVIEW_URL: `https://software.${DOMAIN}`, DEFAULT_TERM_UUID: 'ae6fc4ff-3bd1-4e3f-a987-cc60ab94b422', - DEFAULT_NDA_UUID: '7245bb7d-d7c9-45a0-9603-d5ff05af0977' + DEFAULT_NDA_UUID: '7245bb7d-d7c9-45a0-9603-d5ff05af0977', + SUBMITTER_ROLE_UUID: '732339e7-8e30-49d7-9198-cccf9451e221' } diff --git a/config/constants/production.js b/config/constants/production.js index 2d0c3530..3471b61d 100644 --- a/config/constants/production.js +++ b/config/constants/production.js @@ -22,5 +22,6 @@ module.exports = { CONNECT_APP_URL: `https://connect.${DOMAIN}`, ONLINE_REVIEW_URL: `https://software.${DOMAIN}`, DEFAULT_TERM_UUID: 'ae6fc4ff-3bd1-4e3f-a987-cc60ab94b422', - DEFAULT_NDA_UUID: '7245bb7d-d7c9-45a0-9603-d5ff05af0977' + DEFAULT_NDA_UUID: '7245bb7d-d7c9-45a0-9603-d5ff05af0977', + SUBMITTER_ROLE_UUID: '732339e7-8e30-49d7-9198-cccf9451e221' } diff --git a/src/components/ChallengeEditor/NDAField/index.js b/src/components/ChallengeEditor/NDAField/index.js index b9cf15a5..ff6b1749 100644 --- a/src/components/ChallengeEditor/NDAField/index.js +++ b/src/components/ChallengeEditor/NDAField/index.js @@ -1,10 +1,11 @@ +import _ from 'lodash' import React from 'react' import PropTypes from 'prop-types' import styles from './NDAField.module.scss' import { DEFAULT_NDA_UUID } from '../../../config/constants' const NDAField = ({ challenge, toggleNdaRequire, readOnly }) => { - const isRequiredNda = challenge.terms && challenge.terms.indexOf(DEFAULT_NDA_UUID) >= 0 + const isRequiredNda = challenge.terms && _.some(challenge.terms, { id: DEFAULT_NDA_UUID }) return (
    diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index fada8963..7607bc23 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -11,7 +11,13 @@ import { withRouter } from 'react-router-dom' import { toastr } from 'react-redux-toastr' import xss from 'xss' -import { VALIDATION_VALUE_TYPE, PRIZE_SETS_TYPE, DEFAULT_TERM_UUID, DEFAULT_NDA_UUID } from '../../config/constants' +import { + VALIDATION_VALUE_TYPE, + PRIZE_SETS_TYPE, + DEFAULT_TERM_UUID, + DEFAULT_NDA_UUID, + SUBMITTER_ROLE_UUID +} from '../../config/constants' import { PrimaryButton, OutlineButton } from '../Buttons' import TrackField from './Track-Field' import TypeField from './Type-Field' @@ -390,14 +396,14 @@ class ChallengeEditor extends Component { oldTerms = [] } let newTerms = [] - if (oldTerms.indexOf(DEFAULT_NDA_UUID) >= 0) { - newTerms = _.remove(oldTerms, t => t !== DEFAULT_NDA_UUID) + if (_.some(oldTerms, { id: DEFAULT_NDA_UUID })) { + newTerms = _.remove(oldTerms, t => t.id !== DEFAULT_NDA_UUID) } else { - oldTerms.push(DEFAULT_NDA_UUID) + oldTerms.push({ id: DEFAULT_NDA_UUID, roleId: SUBMITTER_ROLE_UUID }) newTerms = oldTerms } - if (newTerms.indexOf(DEFAULT_TERM_UUID) < 0) { - newTerms.push(DEFAULT_TERM_UUID) + if (!_.some(newTerms, { id: DEFAULT_TERM_UUID })) { + newTerms.push({ id: DEFAULT_TERM_UUID, roleId: SUBMITTER_ROLE_UUID }) } newChallenge.terms = newTerms this.setState({ challenge: newChallenge }) @@ -627,7 +633,7 @@ class ChallengeEditor extends Component { }, descriptionFormat: 'markdown', timelineTemplateId: defaultTemplate.id, - terms: [DEFAULT_TERM_UUID], + terms: [{ id: DEFAULT_TERM_UUID, roleId: SUBMITTER_ROLE_UUID }], phases: this.getTemplatePhases(defaultTemplate), prizeSets: this.getDefaultPrizeSets() } diff --git a/src/config/constants.js b/src/config/constants.js index 6b6ceae4..1d250c0d 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -8,7 +8,8 @@ export const { DIRECT_PROJECT_URL, ONLINE_REVIEW_URL, DEFAULT_TERM_UUID, - DEFAULT_NDA_UUID + DEFAULT_NDA_UUID, + SUBMITTER_ROLE_UUID } = process.env // Actions From bcaefb4905baaa9524a558dff729ae72e095b343 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Jun 2020 17:40:46 +0530 Subject: [PATCH 07/32] =?UTF-8?q?fix:=20git#682=20Console=20error=20when?= =?UTF-8?q?=20rendering=20the=20challenge=20timeline=20=E2=80=94=20removed?= =?UTF-8?q?=20calling=20setState=20from=20render=20method.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChallengeSchedule-Field/index.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index 5f87fc58..49289a01 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -253,25 +253,12 @@ class ChallengeScheduleField extends Component { textProgressContainer.html(textProgressContainer.html()) } - renderTimelineAgain () { - const { isEdit } = this.state - if (!isEdit) { - this.setState({ isEdit: true }, () => { - this.setState({ isEdit: false }) - }) - } - } - render () { const { isEdit } = this.state const { currentTemplate, readOnly } = this.props const { templates, resetPhase, challenge, onUpdateOthers } = this.props const timelines = !isEdit ? this.renderTimeLine() : null const chartHeight = `${(this.getAllPhases().length * GANTT_ROW_HEIGHT) + GANTT_FOOTER_HEIGHT}px` - if (chartHeight !== this.lastChartHeight) { - this.renderTimelineAgain() - } - this.lastChartHeight = chartHeight return (
    From 9492f8e84573f7a49128e9473fa666596be16303 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 5 Jun 2020 17:43:29 +0530 Subject: [PATCH 08/32] =?UTF-8?q?fix:=20git#682=20Console=20error=20when?= =?UTF-8?q?=20rendering=20the=20challenge=20timeline=20=E2=80=94=20removed?= =?UTF-8?q?=20calling=20setState=20from=20render=20method.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/ChallengeEditor/ChallengeSchedule-Field/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index 49289a01..818be9de 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -27,7 +27,6 @@ class ChallengeScheduleField extends Component { this.renderTimeLine = this.renderTimeLine.bind(this) this.getChallengePhase = this.getChallengePhase.bind(this) this.getAllPhases = this.getAllPhases.bind(this) - this.renderTimelineAgain = this.renderTimelineAgain.bind(this) } toggleEditMode () { From 98a4756eae38afda82cf1d7d8eb87601cbab9f53 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 8 Jun 2020 17:51:24 +0530 Subject: [PATCH 09/32] fix: git#683 Status field is not present when editing a challenge --- src/components/ChallengeEditor/index.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 7607bc23..a62a7d3e 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1038,6 +1038,9 @@ class ChallengeEditor extends Component {
    Type: {selectedType ? selectedType.name : ''}
    +
    + Status: {challenge.status} +
    From 8d1ef169aac6612d610e3b0aeea3fe3608ea0984 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 8 Jun 2020 17:51:45 +0530 Subject: [PATCH 10/32] fix: removed console error/warning --- src/components/CopilotCard/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/CopilotCard/index.js b/src/components/CopilotCard/index.js index d171222a..af97c490 100644 --- a/src/components/CopilotCard/index.js +++ b/src/components/CopilotCard/index.js @@ -23,7 +23,7 @@ CopilotCard.defaultProps = { CopilotCard.propTypes = { copilot: PropTypes.shape().isRequired, - selectedCopilot: PropTypes.string.isRequired, + selectedCopilot: PropTypes.string, onUpdateOthers: PropTypes.func } From 42fcead42ab833016ec83fe433207c85d0bbfbf7 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 8 Jun 2020 19:12:28 +0530 Subject: [PATCH 11/32] feat: git#684, Need Back button on challenge edit screen --- .../ChallengeEditor/ChallengeEditor.module.scss | 10 ++++++++++ src/components/ChallengeEditor/index.js | 3 +++ 2 files changed, 13 insertions(+) diff --git a/src/components/ChallengeEditor/ChallengeEditor.module.scss b/src/components/ChallengeEditor/ChallengeEditor.module.scss index b4d1bc59..80915cd7 100644 --- a/src/components/ChallengeEditor/ChallengeEditor.module.scss +++ b/src/components/ChallengeEditor/ChallengeEditor.module.scss @@ -6,6 +6,7 @@ flex-direction: column; margin-bottom: 30px; flex-shrink: 0; + position: relative; } @-moz-document url-prefix() { @@ -236,6 +237,15 @@ padding-bottom: 20px; color: $tc-red } + +.actionButtons { + &.button { + position: absolute; + top: 30px; + right: 20px; + } +} + .buttonContainer { display: flex; margin: 0px 30px; diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index a62a7d3e..06f3d10d 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1120,6 +1120,9 @@ class ChallengeEditor extends Component {
    {getTitle(isNew)}
    +
    + +
    * Required
    { activateModal } From 963f70805338e6a7211f90252016629cced0fa63 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 8 Jun 2020 19:13:51 +0530 Subject: [PATCH 12/32] fix: fixed button size of the last fix git#684, Need Back button on challenge edit screen --- src/components/ChallengeEditor/ChallengeEditor.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ChallengeEditor/ChallengeEditor.module.scss b/src/components/ChallengeEditor/ChallengeEditor.module.scss index 80915cd7..79052a69 100644 --- a/src/components/ChallengeEditor/ChallengeEditor.module.scss +++ b/src/components/ChallengeEditor/ChallengeEditor.module.scss @@ -240,6 +240,7 @@ .actionButtons { &.button { + height: 40px; position: absolute; top: 30px; right: 20px; From cf95b34a02af0c1ca008786fc918e14f117db89a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 9 Jun 2020 18:31:44 +0530 Subject: [PATCH 13/32] fix: git#687 New challenge screen is loading for ever. It seems like something paging related has changed in the api which caused explicit specification of paging params. So, passing page and perPage params, fixes the issue. --- src/services/challenges.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/challenges.js b/src/services/challenges.js index f845daed..2fbed4a8 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -63,7 +63,7 @@ export async function fetchTimelineTemplates () { * @returns {Promise<*>} */ export async function fetchChallengePhases () { - const response = await axiosInstance.get(CHALLENGE_PHASES_URL) + const response = await axiosInstance.get(`${CHALLENGE_PHASES_URL}?page=1&perPage=100`) convertChallengePhaseFromSecondsToHours(response.data) return _.get(response, 'data', []) } From c9665092b4f2ba46e82a0860ae1946a0d83af752 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 9 Jun 2020 21:02:55 +0530 Subject: [PATCH 14/32] fix: Templates are not loading. Cause is default paging changes in the challenge api --- src/services/challenges.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/challenges.js b/src/services/challenges.js index 2fbed4a8..12e0305b 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -54,7 +54,7 @@ export async function fetchGroups (filters) { * @returns {Promise<*>} */ export async function fetchTimelineTemplates () { - const response = await axiosInstance.get(CHALLENGE_TIMELINE_TEMPLATES_URL) + const response = await axiosInstance.get(`${CHALLENGE_TIMELINE_TEMPLATES_URL}?page=1&perPage=100`) return _.get(response, 'data', []) } From 131aa7500ccd1903102d31f2227f6dd742821e61 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 10 Jun 2020 16:30:05 +0530 Subject: [PATCH 15/32] =?UTF-8?q?feat:=20git#678=20Timeline=20Edits=20Not?= =?UTF-8?q?=20Persisting=20=E2=80=94=20add=20logic=20for=20recalculating?= =?UTF-8?q?=20the=20timeline=20by=20copying=20the=20server=20side=20code?= =?UTF-8?q?=20=E2=80=94=20removed=20need=20of=20challengePhasesWithCorrect?= =?UTF-8?q?Timeline=20as=20not=20timeline=20is=20not=20auto=20saved=20and?= =?UTF-8?q?=20hence=20there=20should=20not=20be=20regression=20for=20the?= =?UTF-8?q?=20original=20the=20issue=20git#218=20which=20required=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChallengeSchedule-Field/index.js | 85 +++++++++++++++---- src/components/ChallengeEditor/index.js | 10 --- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index 818be9de..029a1916 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -24,8 +24,10 @@ class ChallengeScheduleField extends Component { isEdit: false } this.toggleEditMode = this.toggleEditMode.bind(this) - this.renderTimeLine = this.renderTimeLine.bind(this) - this.getChallengePhase = this.getChallengePhase.bind(this) + this.prepareTimeline = this.prepareTimeline.bind(this) + this.getPhaseTemplate = this.getPhaseTemplate.bind(this) + this.getPhaseFromTimelineTemplate = this.getPhaseFromTimelineTemplate.bind(this) + this.recalculatePhaseDates = this.recalculatePhaseDates.bind(this) this.getAllPhases = this.getAllPhases.bind(this) } @@ -34,12 +36,35 @@ class ChallengeScheduleField extends Component { this.setState({ isEdit: !isEdit }) } - getChallengePhase (phase) { + /** + * Finds the phase template from the timeline template. Timeline template contains default duration + * and predecessor information. + * + * @param {Object} phase phase for which template is to be found + */ + getPhaseFromTimelineTemplate (phase) { + const { currentTemplate } = this.props + if (currentTemplate.phases) { + let templatePhase = currentTemplate.phases.find(tp => tp.phaseId === phase.phaseId) + if (templatePhase) { + templatePhase = _.cloneDeep(templatePhase) + } + return templatePhase + } + return phase + } + + /** + * Finds the phase definition/template from phase templates. Phase templates contains name and description + * + * @param {Object} phase phase for which definition/template is to be found + */ + getPhaseTemplate (phase) { const { challengePhases } = this.props if (!phase) { return phase } - let challengePhase = challengePhases.find(challengePhase => challengePhase.id === phase.phaseId) + let challengePhase = challengePhases.find(cp => cp.id === phase.phaseId) if (challengePhase) { challengePhase = _.cloneDeep(challengePhase) } @@ -48,14 +73,43 @@ class ChallengeScheduleField extends Component { } getAllPhases () { - const { challenge, challengePhasesWithCorrectTimeline } = this.props - if (challengePhasesWithCorrectTimeline && challengePhasesWithCorrectTimeline.length) { - return challengePhasesWithCorrectTimeline - } + const { challenge } = this.props return challenge.phases } - renderTimeLine () { + /** + * Helper method to recalculate the phase dates. It is used just for rendering the timeline. + * Actual population of dates is done in api at + * https://github.com/topcoder-platform/challenge-api/blob/0253c238d67fddadfa2d6c0fb882568b97ce8a20/src/services/ChallengeService.js#L402 + * + * @param {Object} phase phase for which dates are to be calculated + * @param {Array} phases all phases + * @param {Date} startDate start date of the first phase, usually it is challenge's start date + */ + recalculatePhaseDates (phase, phases, startDate) { + const templatePhase = this.getPhaseFromTimelineTemplate(phase) + if (templatePhase.predecessor) { + const prePhase = _.find(phases, (p) => p.phaseId === templatePhase.predecessor) + // `Predecessor ${templatePhase.predecessor} not found from given phases.` + phase.predecessor = prePhase.id + } + if (!phase.predecessor) { + phase.scheduledStartDate = startDate + phase.scheduledEndDate = moment(startDate).add(phase.duration || 0, 'hours').toDate() + phase.actualStartDate = phase.scheduledStartDate + phase.actualEndDate = phase.scheduledEndDate + } else { + const preIndex = _.findIndex(phases, (p) => p.id === phase.predecessor) + // `Invalid phase predecessor: ${phase.predecessor}` + phase.scheduledStartDate = phases[preIndex].scheduledEndDate + phase.scheduledEndDate = moment(phase.scheduledStartDate).add(phase.duration || 0, 'hours').toDate() + phase.actualStartDate = phase.scheduledStartDate + phase.actualEndDate = phase.scheduledEndDate + } + } + + prepareTimeline () { + const { challenge } = this.props const allPhases = this.getAllPhases() if (_.isEmpty(allPhases) || typeof allPhases[0] === 'undefined') { return null @@ -75,8 +129,11 @@ class ChallengeScheduleField extends Component { ) var hourToMilisecond = 60 * 60 * 1000 // = 1 hour + let cStartDate = challenge.startDate _.map(allPhases, (p, index) => { - const phase = this.getChallengePhase(p) + const phase = this.getPhaseTemplate(p) + // recalculate the phase dates, assuming duration is edited by user + this.recalculatePhaseDates(phase, allPhases, cStartDate) if (phase && timelines) { var startDate if (p.scheduledStartDate) { @@ -101,7 +158,7 @@ class ChallengeScheduleField extends Component { } else { percentage = Math.round(((endDate.getTime() - startDate.getTime()) / (hourToMilisecond * p.duration)) * 100) } - const predecessorPhase = phase.predecessor ? this.getChallengePhase(allPhases.filter(ph => ph.phaseId === phase.predecessor)[0]) : null + const predecessorPhase = phase.predecessor ? this.getPhaseTemplate(allPhases.filter(ph => ph.phaseId === phase.predecessor)[0]) : null timelines.push( [ phase.name || '', @@ -126,7 +183,7 @@ class ChallengeScheduleField extends Component { _.map(challenge.phases, (p, index) => (
    onUpdatePhase(parseInt(newValue), 'duration', index)} @@ -256,7 +313,7 @@ class ChallengeScheduleField extends Component { const { isEdit } = this.state const { currentTemplate, readOnly } = this.props const { templates, resetPhase, challenge, onUpdateOthers } = this.props - const timelines = !isEdit ? this.renderTimeLine() : null + const timelines = !isEdit ? this.prepareTimeline() : null const chartHeight = `${(this.getAllPhases().length * GANTT_ROW_HEIGHT) + GANTT_FOOTER_HEIGHT}px` return (
    @@ -365,7 +422,6 @@ class ChallengeScheduleField extends Component { ChallengeScheduleField.defaultProps = { templates: [], currentTemplate: null, - challengePhasesWithCorrectTimeline: [], removePhase: () => {}, resetPhase: () => {}, onUpdateSelect: () => {}, @@ -377,7 +433,6 @@ ChallengeScheduleField.defaultProps = { ChallengeScheduleField.propTypes = { templates: PropTypes.arrayOf(PropTypes.shape()).isRequired, challengePhases: PropTypes.arrayOf(PropTypes.shape()).isRequired, - challengePhasesWithCorrectTimeline: PropTypes.arrayOf(PropTypes.shape()), challenge: PropTypes.shape().isRequired, removePhase: PropTypes.func, resetPhase: PropTypes.func, diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 06f3d10d..13151985 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -102,7 +102,6 @@ class ChallengeEditor extends Component { this.createDraftHandler = this.createDraftHandler.bind(this) this.onSaveChallenge = this.onSaveChallenge.bind(this) this.getCurrentTemplate = this.getCurrentTemplate.bind(this) - this.getBackendChallengePhases = this.getBackendChallengePhases.bind(this) this.onUpdateMetadata = this.onUpdateMetadata.bind(this) this.getTemplatePhases = this.getTemplatePhases.bind(this) this.autoUpdateChallengeThrottled = _.throttle(this.autoUpdateChallenge.bind(this), 3000) // 3s @@ -848,14 +847,6 @@ class ChallengeEditor extends Component { return _.find(metadata.timelineTemplates, { id: challenge.timelineTemplateId }) } - getBackendChallengePhases () { - const { draftChallenge } = this.state - if (!draftChallenge.data.id) { - return [] - } - return draftChallenge.data.phases - } - render () { const { isLaunch, isConfirm, challenge, isOpenAdvanceSettings, timeLastSaved, isSaving } = this.state const { @@ -1079,7 +1070,6 @@ class ChallengeEditor extends Component { removePhase={this.removePhase} resetPhase={this.resetPhase} challenge={challenge} - challengePhasesWithCorrectTimeline={this.getBackendChallengePhases()} onUpdateSelect={this.onUpdateSelect} onUpdatePhase={this.onUpdatePhase} onUpdateOthers={this.onUpdateOthers} From acd55c094e1247769eca14c61248279a6ec96366 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 10 Jun 2020 19:35:49 +0530 Subject: [PATCH 16/32] =?UTF-8?q?fix:=20git#678=20Timeline=20Edits=20Not?= =?UTF-8?q?=20Persisting=20=E2=80=94=20handled=20NPE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChallengeEditor/ChallengeSchedule-Field/index.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index 029a1916..74bd95f6 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -88,7 +88,10 @@ class ChallengeScheduleField extends Component { */ recalculatePhaseDates (phase, phases, startDate) { const templatePhase = this.getPhaseFromTimelineTemplate(phase) - if (templatePhase.predecessor) { + if (!templatePhase) { + console.warning(`Possible template mismatch. Phase not found in the timeline template of the challenge.`) + } + if (templatePhase && templatePhase.predecessor) { const prePhase = _.find(phases, (p) => p.phaseId === templatePhase.predecessor) // `Predecessor ${templatePhase.predecessor} not found from given phases.` phase.predecessor = prePhase.id From df202def266838250c4411497738a376b4a8511e Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 10 Jun 2020 19:37:14 +0530 Subject: [PATCH 17/32] fix: git#685 Always taking Marathon Match as Timeline Template while updating challenge. --- src/components/ChallengeEditor/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 13151985..9bf17998 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -586,7 +586,7 @@ class ChallengeEditor extends Component { reviewType: challenge.reviewType, track: challenge.track } - challenge.timelineTemplateId = this.props.metadata.timelineTemplates[0].id + challenge.timelineTemplateId = this.getCurrentTemplate().id challenge.projectId = this.props.projectId challenge.prizeSets = challenge.prizeSets.map(p => { const prizes = p.prizes.map(s => ({ ...s, value: convertDollarToInteger(s.value, '$') })) From 6374ae5c7014a6d10bd662709d318834fbbc8129 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 10 Jun 2020 19:40:34 +0530 Subject: [PATCH 18/32] feat: git#665, WIP Filter Timeline Templates based on Selected Type --- config/constants/development.js | 1 + config/constants/production.js | 1 + src/actions/challenges.js | 14 +++++++++++++- src/components/ChallengeEditor/index.js | 24 ++++++++++++++++++++++-- src/containers/ChallengeEditor/index.js | 5 +++++ src/services/challenges.js | 10 ++++++++++ 6 files changed, 52 insertions(+), 3 deletions(-) diff --git a/config/constants/development.js b/config/constants/development.js index 31f3adae..f23bbfe6 100644 --- a/config/constants/development.js +++ b/config/constants/development.js @@ -12,6 +12,7 @@ module.exports = { CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v5/timeline-templates`, CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v5/challenge-types`, CHALLENGE_PHASES_URL: `${DEV_API_HOSTNAME}/v5/challenge-phases`, + CHALLENGE_TIMELINES_URL: `${DEV_API_HOSTNAME}/v5/challenge-timelines`, PROJECT_API_URL: `${DEV_API_HOSTNAME}/v5/projects`, GROUPS_API_URL: `${DEV_API_HOSTNAME}/v5/groups`, TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`, diff --git a/config/constants/production.js b/config/constants/production.js index 3471b61d..1f46e954 100644 --- a/config/constants/production.js +++ b/config/constants/production.js @@ -12,6 +12,7 @@ module.exports = { CHALLENGE_TIMELINE_TEMPLATES_URL: `${PROD_API_HOSTNAME}/v5/timeline-templates`, CHALLENGE_TYPES_URL: `${PROD_API_HOSTNAME}/v5/challenge-types`, CHALLENGE_PHASES_URL: `${PROD_API_HOSTNAME}/v5/challenge-phases`, + CHALLENGE_TIMELINES_URL: `${DEV_API_HOSTNAME}/v5/challenge-timelines`, PROJECT_API_URL: `${PROD_API_HOSTNAME}/v5/projects`, GROUPS_API_URL: `${PROD_API_HOSTNAME}/v5/groups`, TERMS_API_URL: `${PROD_API_HOSTNAME}/v5/terms`, diff --git a/src/actions/challenges.js b/src/actions/challenges.js index d29ee175..042406c1 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -10,7 +10,8 @@ import { fetchChallenges, fetchChallengeTerms, fetchResources, - fetchResourceRoles + fetchResourceRoles, + fetchChallengeTimelines } from '../services/challenges' import { LOAD_CHALLENGE_DETAILS_PENDING, @@ -228,6 +229,17 @@ export function loadChallengeTypes () { } } +export function loadChallengeTimelines () { + return async (dispatch) => { + const challengeTimelines = await fetchChallengeTimelines() + dispatch({ + type: LOAD_CHALLENGE_METADATA_SUCCESS, + metadataKey: 'challengeTimelines', + metadataValue: challengeTimelines + }) + } +} + export function loadChallengeTags () { return async (dispatch) => { const challengeTags = await fetchChallengeTags() diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 9bf17998..5c517eeb 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -104,6 +104,7 @@ class ChallengeEditor extends Component { this.getCurrentTemplate = this.getCurrentTemplate.bind(this) this.onUpdateMetadata = this.onUpdateMetadata.bind(this) this.getTemplatePhases = this.getTemplatePhases.bind(this) + this.getAvailableTimelineTemplates = this.getAvailableTimelineTemplates.bind(this) this.autoUpdateChallengeThrottled = _.throttle(this.autoUpdateChallenge.bind(this), 3000) // 3s this.resetChallengeData((newState, finish) => { this.state = { @@ -617,8 +618,13 @@ class ChallengeEditor extends Component { if (!this.props.isNew) return const { metadata } = this.props const { name, track, typeId } = this.state.challenge + const { timelineTemplates } = metadata - const defaultTemplate = _.find(metadata.timelineTemplates, { name: 'Standard Development' }) + // fallback template + const STD_DEV_TIMELINE_TEMPLATE = _.find(timelineTemplates, { name: 'Standard Development' }) + const avlTemplates = this.getAvailableTimelineTemplates() + // chooses first available timeline template or fallback template for the new challenge + const defaultTemplate = avlTemplates && avlTemplates.length > 0 ? avlTemplates[0] : STD_DEV_TIMELINE_TEMPLATE const newChallenge = { status: 'New', @@ -847,6 +853,20 @@ class ChallengeEditor extends Component { return _.find(metadata.timelineTemplates, { id: challenge.timelineTemplateId }) } + /** + * Filters the available timeline templates based on the challenge type + */ + getAvailableTimelineTemplates () { + const { challenge } = this.state + const { metadata } = this.props + const { challengeTimelines, timelineTemplates } = metadata + + // all timeline template ids available for the challenge type + const availableTemplateIds = _.filter(challengeTimelines, tt => tt.typeId === challenge.typeId).map(tt => tt.timelineTemplateId) + // filter and return timeline templates that are available for this challenge type + return _.filter(timelineTemplates, tt => availableTemplateIds.indexOf(tt.id) !== -1) + } + render () { const { isLaunch, isConfirm, challenge, isOpenAdvanceSettings, timeLastSaved, isSaving } = this.state const { @@ -1065,7 +1085,7 @@ class ChallengeEditor extends Component { )} } + */ +export async function fetchChallengeTimelines () { + const response = await axiosInstance.get(`${CHALLENGE_TIMELINES_URL}?page=1&perPage=100`) + return _.get(response, 'data', []) +} + /** * Api request for fetching challenge phases * @returns {Promise<*>} From 8bbd853d292ab7344f1455eafe2487e87a415a4b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 10 Jun 2020 22:01:47 +0530 Subject: [PATCH 19/32] fix: typo --- src/components/ChallengeEditor/ChallengeSchedule-Field/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index 74bd95f6..d0a1a874 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -89,7 +89,7 @@ class ChallengeScheduleField extends Component { recalculatePhaseDates (phase, phases, startDate) { const templatePhase = this.getPhaseFromTimelineTemplate(phase) if (!templatePhase) { - console.warning(`Possible template mismatch. Phase not found in the timeline template of the challenge.`) + console.warn(`Possible template mismatch. Phase not found in the timeline template of the challenge.`) } if (templatePhase && templatePhase.predecessor) { const prePhase = _.find(phases, (p) => p.phaseId === templatePhase.predecessor) From 1cd61688c93bba1a30231719fdea2fc40f154f75 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 17 Jun 2020 14:02:08 +0530 Subject: [PATCH 20/32] fix: git#690, No projects shown in the landing page - for now called the project load on page load. Ideally project listing should not be part of challenge manager app. CM should work only when a project is already selected. --- src/containers/Sidebar/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Sidebar/index.js b/src/containers/Sidebar/index.js index ea8fa1ef..b8764a94 100644 --- a/src/containers/Sidebar/index.js +++ b/src/containers/Sidebar/index.js @@ -15,7 +15,7 @@ class SidebarContainer extends Component { } componentDidMount () { - // this.props.loadProjects() + this.props.loadProjects() const { projectId, activeProjectId } = this.props From 08c89588e18fab61183851234a47f91df760310b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Wed, 17 Jun 2020 17:38:24 +0530 Subject: [PATCH 21/32] fix: git##696 Updated placeholder for the search text box --- src/containers/Challenges/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/containers/Challenges/index.js b/src/containers/Challenges/index.js index 4bd3d69e..454d85dd 100644 --- a/src/containers/Challenges/index.js +++ b/src/containers/Challenges/index.js @@ -110,7 +110,7 @@ class Challenges extends Component { this.updateProjectName(e.target.value)} value={searchProjectName} /> From 537ac3879ac32ea322527b4c6b766ce27ba14a95 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 19 Jun 2020 10:45:59 +0530 Subject: [PATCH 22/32] feat: git#668 Restyled the Edit button for the timeline component to avoid confusion coming with accordion kind of UX. Also added Save phases button for explicitly saving changes in the phases, this is in alignment with `Reset Phases` button. --- .../ChallengeSchedule-Field.module.scss | 4 +++ .../ChallengeSchedule-Field/index.js | 29 +++++++++++++------ src/components/ChallengeEditor/index.js | 10 +++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/ChallengeSchedule-Field.module.scss b/src/components/ChallengeEditor/ChallengeSchedule-Field/ChallengeSchedule-Field.module.scss index 5884e98a..8afe2400 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/ChallengeSchedule-Field.module.scss +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/ChallengeSchedule-Field.module.scss @@ -6,6 +6,10 @@ flex-direction: column; z-index: 3; + .actionButtons { + display: flex; + } + .PhaseRow { display: flex; align-items: center; diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index d0a1a874..e7871c1a 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -11,7 +11,7 @@ import Chart from 'react-google-charts' import Select from '../../Select' import { parseSVG } from '../../../util/svg' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faAngleDown, faTrash } from '@fortawesome/free-solid-svg-icons' +import { faTrash } from '@fortawesome/free-solid-svg-icons' import PrimaryButton from '../../Buttons/PrimaryButton' const GANTT_ROW_HEIGHT = 45 @@ -314,8 +314,8 @@ class ChallengeScheduleField extends Component { render () { const { isEdit } = this.state - const { currentTemplate, readOnly } = this.props - const { templates, resetPhase, challenge, onUpdateOthers } = this.props + const { currentTemplate, readOnly, templates } = this.props + const { savePhases, resetPhase, challenge, onUpdateOthers } = this.props const timelines = !isEdit ? this.prepareTimeline() : null const chartHeight = `${(this.getAllPhases().length * GANTT_ROW_HEIGHT) + GANTT_FOOTER_HEIGHT}px` return ( @@ -369,12 +369,17 @@ class ChallengeScheduleField extends Component { Timezone: {jstz.determine().name()}
    -
    -
    - Edit - + { !readOnly && + (
    +
    + +
    -
    + ) + }
    { @@ -407,7 +412,11 @@ class ChallengeScheduleField extends Component { ) } {currentTemplate && isEdit && !readOnly && (
    -
    +
    + savePhases()} /> {}, resetPhase: () => {}, + savePhases: () => {}, onUpdateSelect: () => {}, onUpdatePhase: () => {}, onUpdateOthers: () => {}, @@ -439,6 +449,7 @@ ChallengeScheduleField.propTypes = { challenge: PropTypes.shape().isRequired, removePhase: PropTypes.func, resetPhase: PropTypes.func, + savePhases: PropTypes.func, onUpdateSelect: PropTypes.func, onUpdatePhase: PropTypes.func, onUpdateOthers: PropTypes.func.isRequired, diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 5c517eeb..95aad734 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -87,6 +87,7 @@ class ChallengeEditor extends Component { this.removeAttachment = this.removeAttachment.bind(this) this.removePhase = this.removePhase.bind(this) this.resetPhase = this.resetPhase.bind(this) + this.savePhases = this.savePhases.bind(this) this.toggleLaunch = this.toggleLaunch.bind(this) this.onUpdateMultiSelect = this.onUpdateMultiSelect.bind(this) this.onUpdatePhase = this.onUpdatePhase.bind(this) @@ -430,6 +431,14 @@ class ChallengeEditor extends Component { newChallenge.phases = _.clone(newPhaseList) this.setState({ challenge: newChallenge }) } + + /** + * Save updated challenge Phases + */ + async savePhases () { + this.autoUpdateChallengeThrottled('phases') + } + /** * Reset challenge Phases */ @@ -1089,6 +1098,7 @@ class ChallengeEditor extends Component { challengePhases={metadata.challengePhases} removePhase={this.removePhase} resetPhase={this.resetPhase} + savePhases={this.savePhases} challenge={challenge} onUpdateSelect={this.onUpdateSelect} onUpdatePhase={this.onUpdatePhase} From d261b0d1becc1297b08ab70d4adb3919106defcb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 19 Jun 2020 15:34:38 +0530 Subject: [PATCH 23/32] feat: git#668 Added confirmation modal for save phase operation --- src/components/ChallengeEditor/index.js | 100 +++++++++--------- src/components/Modal/AlertModal.js | 46 ++++++++ src/components/Modal/ConfirmationModal.js | 44 ++++++++ .../Modal/ConfirmationModal.module.scss | 92 ++++++++++++++++ 4 files changed, 230 insertions(+), 52 deletions(-) create mode 100644 src/components/Modal/AlertModal.js create mode 100644 src/components/Modal/ConfirmationModal.js create mode 100644 src/components/Modal/ConfirmationModal.module.scss diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 95aad734..2b17224e 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -6,7 +6,7 @@ import { Helmet } from 'react-helmet' import cn from 'classnames' import moment from 'moment' import { pick } from 'lodash/fp' -import Modal from '../Modal' +// import Modal from '../Modal' import { withRouter } from 'react-router-dom' import { toastr } from 'react-redux-toastr' import xss from 'xss' @@ -46,6 +46,8 @@ import { deleteResource, patchChallenge } from '../../services/challenges' +import ConfirmationModal from '../Modal/ConfirmationModal' +import AlertModal from '../Modal/AlertModal' const theme = { container: styles.modalContainer @@ -436,7 +438,11 @@ class ChallengeEditor extends Component { * Save updated challenge Phases */ async savePhases () { - this.autoUpdateChallengeThrottled('phases') + await this.autoUpdateChallengeThrottled('phases') + this.setState({ + isConfirm: true, + isLaunch: true + }) } /** @@ -923,6 +929,7 @@ class ChallengeEditor extends Component { let activateModal = null let draftModal = null + let savedModal = null let { type } = challenge if (!type) { @@ -934,63 +941,51 @@ class ChallengeEditor extends Component { } } } + console.log(isNew, 'isNew') + if (!isNew && challenge.status === 'New' && isLaunch && isConfirm) { + savedModal = ( + + ) + } if (!isNew && isLaunch && !isConfirm) { activateModal = ( - -
    -
    Launch Challenge Confirmation
    - {`Do you want to launch ${type} challenge "${challenge.name}"?`} -
    -
    - -
    -
    - -
    -
    -
    -
    + ) } - if (!isNew && isLaunch && isConfirm) { + if (!isNew && challenge.status !== 'New' && isLaunch && isConfirm) { draftModal = ( - this.resetModal()}> -
    -
    Success
    - { - challenge.status === 'Draft' - ? 'Your challenge is saved as draft' - : 'We have scheduled your challenge and processed the payment' - } -
    -
    - -
    -
    - -
    -
    -
    -
    + ) } @@ -1147,6 +1142,7 @@ class ChallengeEditor extends Component {
    { activateModal } { draftModal } + { savedModal }
    { challengeForm }
    diff --git a/src/components/Modal/AlertModal.js b/src/components/Modal/AlertModal.js new file mode 100644 index 00000000..fd12b4de --- /dev/null +++ b/src/components/Modal/AlertModal.js @@ -0,0 +1,46 @@ +import React from 'react' +import PropTypes from 'prop-types' +import cn from 'classnames' +import Modal from '.' +import styles from './ConfirmationModal.module.scss' +import OutlineButton from '../Buttons/OutlineButton' +import PrimaryButton from '../Buttons/PrimaryButton' + +const AlertModal = ({ title, message, theme, onClose, closeLink, okLink, closeText, okText }) => ( + +
    +
    {title}
    + {message} +
    +
    + {} : onClose} + /> +
    +
    + +
    +
    +
    +
    +) + +AlertModal.propTypes = { + title: PropTypes.string, + message: PropTypes.string, + theme: PropTypes.shape(), + onClose: PropTypes.func, + closeText: PropTypes.string, + closeLink: PropTypes.string, + okText: PropTypes.string, + okLink: PropTypes.string +} + +export default AlertModal diff --git a/src/components/Modal/ConfirmationModal.js b/src/components/Modal/ConfirmationModal.js new file mode 100644 index 00000000..94b646ea --- /dev/null +++ b/src/components/Modal/ConfirmationModal.js @@ -0,0 +1,44 @@ +import React from 'react' +import PropTypes from 'prop-types' +import cn from 'classnames' +import Modal from '.' +import styles from './ConfirmationModal.module.scss' +import OutlineButton from '../Buttons/OutlineButton' +import PrimaryButton from '../Buttons/PrimaryButton' + +const ConfirmationModal = ({ title, message, theme, isProcessing, onCancel, onConfirm }) => ( + +
    +
    {title}
    + {message} +
    +
    + +
    +
    + +
    +
    +
    +
    +) + +ConfirmationModal.propTypes = { + title: PropTypes.string, + message: PropTypes.string, + theme: PropTypes.shape(), + isProcessing: PropTypes.bool, + onCancel: PropTypes.func, + onConfirm: PropTypes.func +} + +export default ConfirmationModal diff --git a/src/components/Modal/ConfirmationModal.module.scss b/src/components/Modal/ConfirmationModal.module.scss new file mode 100644 index 00000000..ad5bd4b6 --- /dev/null +++ b/src/components/Modal/ConfirmationModal.module.scss @@ -0,0 +1,92 @@ +@import "../../styles/includes"; + +.contentContainer { + box-sizing: border-box; + background: $white; + opacity: 1; + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + border-radius: 6px; + margin: 0 auto; + width: 852px; + padding: 30px; + + .content { + padding: 30px; + width: 100%; + height: 100%; + } + + .title { + @include roboto-bold(); + + font-size: 30px; + line-height: 36px; + margin-bottom: 30px; + margin-top: 0; + } + + span { + @include roboto; + + font-size: 22px; + font-weight: 400; + line-height: 26px; + } + + &.confirm { + width: 999px; + + .buttonGroup { + display: flex; + justify-content: space-between; + margin-top: 30px; + + .buttonSizeA { + width: 193px; + height: 40px; + margin-right: 33px; + + span { + font-size: 18px; + font-weight: 500; + } + } + + .buttonSizeB{ + width: 160px; + height: 40px; + + span { + font-size: 18px; + font-weight: 500; + line-height: 22px; + } + } + } + } + + .buttonGroup { + display: flex; + justify-content: space-between; + margin-top: 30px; + + .button { + width: 135px; + height: 40px; + margin-right: 66px; + + span { + font-size: 18px; + font-weight: 500; + } + } + + .button:last-child { + margin-right: 0; + } + } +} \ No newline at end of file From a516ad071909c6a3ec831841f5b7cb94c3d6dfe7 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Fri, 19 Jun 2020 16:15:33 +0530 Subject: [PATCH 24/32] fix: git#705 fixed overflowing text --- .../Description-Field/Description-Field.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/ChallengeEditor/Description-Field/Description-Field.module.scss b/src/components/ChallengeEditor/Description-Field/Description-Field.module.scss index c888e6f6..f303a246 100644 --- a/src/components/ChallengeEditor/Description-Field/Description-Field.module.scss +++ b/src/components/ChallengeEditor/Description-Field/Description-Field.module.scss @@ -10,6 +10,7 @@ max-height: 410px; display: flex; flex-direction: column; + overflow: auto; &.isPrivate { max-height: 205px; From 494a24111e451181a297b972cbb0d94ec3dce893 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Mon, 22 Jun 2020 18:04:51 +0530 Subject: [PATCH 25/32] fix: Removed projects loading on challenges pages fix: challenges loading on home page --- src/components/ChallengeEditor/index.js | 1 - src/containers/Challenges/index.js | 7 ++----- src/containers/Sidebar/index.js | 14 +++++++++++--- src/reducers/challenges.js | 2 +- src/reducers/sidebar.js | 2 +- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 2b17224e..d8458b05 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -941,7 +941,6 @@ class ChallengeEditor extends Component { } } } - console.log(isNew, 'isNew') if (!isNew && challenge.status === 'New' && isLaunch && isConfirm) { savedModal = ( Date: Tue, 30 Jun 2020 10:16:11 +0530 Subject: [PATCH 26/32] fix: git#707 New Challenge - Back button should take user to Challenge listing --- src/components/ChallengeEditor/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index d8458b05..78d1c35a 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1135,7 +1135,7 @@ class ChallengeEditor extends Component {
    {getTitle(isNew)}
    - +
    * Required
    From e231dc4630b702f2e65355e608a0e3e31a9ac88b Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 30 Jun 2020 10:17:38 +0530 Subject: [PATCH 27/32] fix: NPE fix (Ideally it should not happen but because of some weird bug with backend, it seems like data is being reset for a challenge) --- src/components/ChallengeEditor/ChallengeSchedule-Field/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js index e7871c1a..d0a4e5c8 100644 --- a/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeSchedule-Field/index.js @@ -44,7 +44,7 @@ class ChallengeScheduleField extends Component { */ getPhaseFromTimelineTemplate (phase) { const { currentTemplate } = this.props - if (currentTemplate.phases) { + if (currentTemplate && currentTemplate.phases) { let templatePhase = currentTemplate.phases.find(tp => tp.phaseId === phase.phaseId) if (templatePhase) { templatePhase = _.cloneDeep(templatePhase) From 8ff64cb2898367d112d69a1c3912a54901eeedcb Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 30 Jun 2020 10:34:36 +0530 Subject: [PATCH 28/32] fix: git#708, ChallengeEditor container - Remove potentially unused code --- src/containers/ChallengeEditor/index.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index 7e3459a8..6fe3f152 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -48,17 +48,17 @@ class ChallengeEditor extends Component { loadResourceRoles() this.fetchChallengeDetails(match, loadChallengeDetails, loadResources) - this.unlisten = this.props.history.listen(() => { - const { isLoading } = this.props - if (!isLoading) { - const { match: newMatch, loadChallengeDetails, loadResources } = this.props - this.fetchChallengeDetails(newMatch, loadChallengeDetails, loadResources) - } - }) + // this.unlisten = this.props.history.listen(() => { + // const { isLoading } = this.props + // if (!isLoading) { + // const { match: newMatch, loadChallengeDetails, loadResources } = this.props + // this.fetchChallengeDetails(newMatch, loadChallengeDetails, loadResources) + // } + // }) } componentWillUnmount () { - this.unlisten() + // this.unlisten() } componentWillReceiveProps (nextProps) { @@ -179,7 +179,7 @@ ChallengeEditor.propTypes = { challengeResources: PropTypes.arrayOf(PropTypes.object), challengeDetails: PropTypes.object, projectDetail: PropTypes.object, - history: PropTypes.object, + // history: PropTypes.object, metadata: PropTypes.shape({ challengeTypes: PropTypes.array }), From a66cabb9e5e31a32c64f26c44c72872a4808d3f2 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 30 Jun 2020 10:41:45 +0530 Subject: [PATCH 29/32] feat: git#706 Disable editing timeline template for a challenge - Hidden the challenge schedule field until we have a fix --- .../ChallengeEditor/ChallengeView/index.js | 20 +++++++------- src/components/ChallengeEditor/index.js | 27 ++++++++++--------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index 26dfdd86..a5c7d067 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -50,7 +50,7 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources, const isInternal = reviewType === 'internal' const timeLineTemplate = _.find(metadata.timelineTemplates, { id: challenge.timelineTemplateId }) if (isLoading || _.isEmpty(metadata.challengePhases) || challenge.id !== challengeId) return - + const showTimeline = false // disables the timeline for time being https://github.com/topcoder-platform/challenge-engine-ui/issues/706 return (
    @@ -123,14 +123,16 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources, Groups: {challenge.groups ? challenge.groups.join(', ') : ''}
    )} - + { showTimeline && ( + + )}
    Public specification *
    diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index 78d1c35a..f8769c04 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -1022,6 +1022,7 @@ class ChallengeEditor extends Component { const selectedType = _.find(metadata.challengeTypes, { id: challenge.typeId }) const currentChallengeId = this.getCurrentChallengeId() + const showTimeline = false // disables the timeline for time being https://github.com/topcoder-platform/challenge-engine-ui/issues/706 const challengeForm = isNew ? (
    @@ -1087,18 +1088,20 @@ class ChallengeEditor extends Component { )} - + { showTimeline && ( + + )}
    Public specification *
    From 47ac3aea5389fb561fed2f6a2d2872f918183c25 Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 30 Jun 2020 14:51:17 +0530 Subject: [PATCH 30/32] feat: git#700 Popup message or toaster is missing to notify user if there is any server side error --- src/components/ChallengeEditor/index.js | 6 ++++-- src/components/Modal/ConfirmationModal.js | 4 +++- src/components/Modal/ConfirmationModal.module.scss | 9 +++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index f8769c04..eac183e2 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -186,7 +186,7 @@ class ChallengeEditor extends Component { } resetModal () { - this.setState({ isLoading: false, isConfirm: false, isLaunch: false }) + this.setState({ isLoading: false, isConfirm: false, isLaunch: false, error: null }) } onUpdateDescription (description, fieldName) { @@ -786,7 +786,8 @@ class ChallengeEditor extends Component { challenge: newChallenge, isSaving: false }, cb) } catch (e) { - this.setState({ isSaving: false }, cb) + const error = _.get(e, 'response.data.message', `Unable to update the challenge to status ${status}`) + this.setState({ isSaving: false, error }, cb) } } @@ -963,6 +964,7 @@ class ChallengeEditor extends Component { message={`Do you want to launch ${type} challenge "${challenge.name}"?`} theme={theme} isProcessing={this.state.isSaving} + errorMessage={this.state.error} onCancel={this.resetModal} onConfirm={this.onActiveChallenge} /> diff --git a/src/components/Modal/ConfirmationModal.js b/src/components/Modal/ConfirmationModal.js index 94b646ea..60e5b23d 100644 --- a/src/components/Modal/ConfirmationModal.js +++ b/src/components/Modal/ConfirmationModal.js @@ -6,7 +6,7 @@ import styles from './ConfirmationModal.module.scss' import OutlineButton from '../Buttons/OutlineButton' import PrimaryButton from '../Buttons/PrimaryButton' -const ConfirmationModal = ({ title, message, theme, isProcessing, onCancel, onConfirm }) => ( +const ConfirmationModal = ({ title, message, errorMessage, theme, isProcessing, onCancel, onConfirm }) => (
    {title}
    @@ -28,6 +28,7 @@ const ConfirmationModal = ({ title, message, theme, isProcessing, onCancel, onCo />
    + {errorMessage}
    ) @@ -35,6 +36,7 @@ const ConfirmationModal = ({ title, message, theme, isProcessing, onCancel, onCo ConfirmationModal.propTypes = { title: PropTypes.string, message: PropTypes.string, + errorMessage: PropTypes.string, theme: PropTypes.shape(), isProcessing: PropTypes.bool, onCancel: PropTypes.func, diff --git a/src/components/Modal/ConfirmationModal.module.scss b/src/components/Modal/ConfirmationModal.module.scss index ad5bd4b6..fc49b22d 100644 --- a/src/components/Modal/ConfirmationModal.module.scss +++ b/src/components/Modal/ConfirmationModal.module.scss @@ -29,6 +29,15 @@ margin-top: 0; } + .message { + + } + + .errorMessage { + color: #BE405E; + font-size: 14px; + } + span { @include roboto; From dca8b6730b44bda43975a39ce208d0af0410756a Mon Sep 17 00:00:00 2001 From: Vikas Agarwal Date: Tue, 30 Jun 2020 15:11:29 +0530 Subject: [PATCH 31/32] fix: git#709 Type of an old project is not shown, if we deprecated that type later on --- src/components/ChallengeEditor/Type-Field/index.js | 3 ++- src/services/challenges.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/ChallengeEditor/Type-Field/index.js b/src/components/ChallengeEditor/Type-Field/index.js index f694c569..3dbd2bdc 100644 --- a/src/components/ChallengeEditor/Type-Field/index.js +++ b/src/components/ChallengeEditor/Type-Field/index.js @@ -1,3 +1,4 @@ +import _ from 'lodash' import React from 'react' import PropTypes from 'prop-types' import Select from '../../Select' @@ -14,7 +15,7 @@ const TypeField = ({ types, onUpdateSelect, challenge, disabled }) => {