diff --git a/config/constants/development.js b/config/constants/development.js
index 15478e88..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`,
@@ -23,5 +24,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..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`,
@@ -22,5 +23,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/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/challenges.js b/src/actions/challenges.js
index 0ddae369..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,
@@ -70,8 +71,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,
@@ -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/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/ChallengeEditor/ChallengeEditor.module.scss b/src/components/ChallengeEditor/ChallengeEditor.module.scss
index b4d1bc59..79052a69 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,16 @@
padding-bottom: 20px;
color: $tc-red
}
+
+.actionButtons {
+ &.button {
+ height: 40px;
+ position: absolute;
+ top: 30px;
+ right: 20px;
+ }
+}
+
.buttonContainer {
display: flex;
margin: 0px 30px;
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/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 5f87fc58..d0a4e5c8 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
@@ -24,10 +24,11 @@ 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)
- this.renderTimelineAgain = this.renderTimelineAgain.bind(this)
}
toggleEditMode () {
@@ -35,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 && 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)
}
@@ -49,14 +73,46 @@ 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) {
+ 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)
+ // `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
@@ -76,8 +132,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) {
@@ -102,7 +161,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 || '',
@@ -127,7 +186,7 @@ class ChallengeScheduleField extends Component {
_.map(challenge.phases, (p, index) => (
onUpdatePhase(parseInt(newValue), 'duration', index)}
@@ -253,25 +312,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 { 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`
- if (chartHeight !== this.lastChartHeight) {
- this.renderTimelineAgain()
- }
- this.lastChartHeight = chartHeight
return (
@@ -323,12 +369,17 @@ class ChallengeScheduleField extends Component {
Timezone: {jstz.determine().name()}
-
-
-
Edit
-
+ { !readOnly &&
+ (
-
+ )
+ }
{
@@ -361,7 +412,11 @@ class ChallengeScheduleField extends Component {
)
}
{currentTemplate && isEdit && !readOnly && (
-
+
+
savePhases()} />
{},
resetPhase: () => {},
+ savePhases: () => {},
onUpdateSelect: () => {},
onUpdatePhase: () => {},
onUpdateOthers: () => {},
@@ -391,10 +446,10 @@ 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,
+ savePhases: PropTypes.func,
onUpdateSelect: PropTypes.func,
onUpdatePhase: PropTypes.func,
onUpdateOthers: PropTypes.func.isRequired,
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..a5c7d067 100644
--- a/src/components/ChallengeEditor/ChallengeView/index.js
+++ b/src/components/ChallengeEditor/ChallengeView/index.js
@@ -50,13 +50,14 @@ 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 (
View Details
-
+
@@ -122,14 +123,16 @@ const ChallengeView = ({ projectDetail, challenge, metadata, challengeResources,
Groups: {challenge.groups ? challenge.groups.join(', ') : ''}
)}
-
+ { showTimeline && (
+
+ )}
Public specification *
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/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;
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/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/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 }) => {
@@ -1064,18 +1090,20 @@ class ChallengeEditor extends Component {
)}
-
+ { showTimeline && (
+
+ )}
Public specification *
@@ -1111,10 +1139,14 @@ class ChallengeEditor extends Component {
{getTitle(isNew)}
+
* Required
{ activateModal }
{ draftModal }
+ { savedModal }
{ challengeForm }
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 (
+ -
+
+
+ )
})
}
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
}
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}
+
+
+
+)
+
+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..60e5b23d
--- /dev/null
+++ b/src/components/Modal/ConfirmationModal.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 ConfirmationModal = ({ title, message, errorMessage, theme, isProcessing, onCancel, onConfirm }) => (
+
+
+
{title}
+
{message}
+
+
{errorMessage}
+
+
+)
+
+ConfirmationModal.propTypes = {
+ title: PropTypes.string,
+ message: PropTypes.string,
+ errorMessage: 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..fc49b22d
--- /dev/null
+++ b/src/components/Modal/ConfirmationModal.module.scss
@@ -0,0 +1,101 @@
+@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;
+ }
+
+ .message {
+
+ }
+
+ .errorMessage {
+ color: #BE405E;
+ font-size: 14px;
+ }
+
+ 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
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 ? : (
-
- )
- }
)
}
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/config/constants.js b/src/config/constants.js
index e587c2f8..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
@@ -71,12 +72,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 = [
diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js
index 284fa820..6fe3f152 100644
--- a/src/containers/ChallengeEditor/index.js
+++ b/src/containers/ChallengeEditor/index.js
@@ -10,6 +10,7 @@ import {
loadTimelineTemplates,
loadChallengePhases,
loadChallengeTypes,
+ loadChallengeTimelines,
loadChallengeTags,
loadChallengeTerms,
loadGroups,
@@ -29,6 +30,7 @@ class ChallengeEditor extends Component {
loadTimelineTemplates,
loadChallengePhases,
loadChallengeTypes,
+ loadChallengeTimelines,
loadChallengeTags,
loadChallengeTerms,
loadGroups,
@@ -39,23 +41,24 @@ class ChallengeEditor extends Component {
loadTimelineTemplates()
loadChallengePhases()
loadChallengeTypes()
+ loadChallengeTimelines()
loadChallengeTags()
loadChallengeTerms()
loadGroups()
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) {
@@ -166,6 +169,7 @@ ChallengeEditor.propTypes = {
loadTimelineTemplates: PropTypes.func,
loadChallengePhases: PropTypes.func,
loadChallengeTypes: PropTypes.func,
+ loadChallengeTimelines: PropTypes.func,
loadChallengeTags: PropTypes.func,
loadChallengeTerms: PropTypes.func,
loadGroups: PropTypes.func,
@@ -175,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
}),
@@ -203,6 +207,7 @@ const mapDispatchToProps = {
loadTimelineTemplates,
loadChallengePhases,
loadChallengeTypes,
+ loadChallengeTimelines,
loadChallengeTags,
loadGroups,
createAttachment,
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..f2ec3292 100644
--- a/src/containers/Challenges/index.js
+++ b/src/containers/Challenges/index.js
@@ -2,25 +2,40 @@
* 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
if (menu === 'NULL' && activeProjectId !== -1) {
resetSidebarActiveParams()
} else {
- this.props.loadChallengesByPage(1, projectId ? parseInt(projectId) : -1, CHALLENGE_STATUS.ACTIVE, '')
+ // this.props.loadChallengesByPage(1, projectId ? parseInt(projectId) : -1, CHALLENGE_STATUS.ACTIVE, '')
if (projectId) {
this.props.loadProject(projectId)
}
@@ -28,7 +43,9 @@ class Challenges extends Component {
}
componentWillReceiveProps (nextProps) {
- this.reloadChallenges(nextProps)
+ if (this.props.activeProjectId !== nextProps.activeProjectId) {
+ this.reloadChallenges(nextProps)
+ }
}
reloadChallenges (props) {
@@ -43,6 +60,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 +84,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 ?
: (
+
+ )
+ }
+
+ { activeProjectId !== -1 &&
+ }
+
)
}
}
@@ -96,7 +168,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 +185,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..b58b340f 100644
--- a/src/containers/Sidebar/index.js
+++ b/src/containers/Sidebar/index.js
@@ -15,15 +15,23 @@ class SidebarContainer extends Component {
}
componentDidMount () {
- this.props.loadProjects()
-
- const { projectId, activeProjectId } = this.props
+ const { projectId, activeProjectId, isLoading } = this.props
+ if (!projectId && activeProjectId === -1 && !isLoading) {
+ this.props.loadProjects()
+ }
if (projectId && activeProjectId < 0) {
this.props.setActiveProject(parseInt(projectId))
}
}
+ componentWillReceiveProps (nextProps) {
+ const { projectId, isLoading } = nextProps
+ if (this.props.projectId !== projectId && !projectId && !isLoading) {
+ this.props.loadProjects()
+ }
+ }
+
updateProjectName (val) {
this.setState({ searchProjectName: val })
this.props.loadProjects(val)
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/challenges.js b/src/reducers/challenges.js
index f494756c..5ade02c6 100644
--- a/src/reducers/challenges.js
+++ b/src/reducers/challenges.js
@@ -23,7 +23,7 @@ import {
} from '../config/constants'
const initialState = {
- isLoading: true,
+ isLoading: false,
challenges: [],
metadata: {},
challengeDetails: {},
diff --git a/src/reducers/sidebar.js b/src/reducers/sidebar.js
index 5e775474..54e2cf45 100644
--- a/src/reducers/sidebar.js
+++ b/src/reducers/sidebar.js
@@ -11,14 +11,14 @@ import {
const initialState = {
activeProjectId: -1,
- isLoading: true,
+ isLoading: false,
projects: []
}
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 */}
diff --git a/src/services/challenges.js b/src/services/challenges.js
index 9fa6150c..0e29d600 100644
--- a/src/services/challenges.js
+++ b/src/services/challenges.js
@@ -1,13 +1,14 @@
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,
CHALLENGE_TYPES_URL,
CHALLENGE_TIMELINE_TEMPLATES_URL,
CHALLENGE_PHASES_URL,
+ CHALLENGE_TIMELINES_URL,
GROUPS_API_URL,
PLATFORMS_V4_API_URL,
TECHNOLOGIES_V4_API_URL,
@@ -21,7 +22,7 @@ const {
* @returns {Promise<*>}
*/
export async function fetchChallengeTypes () {
- const response = await axiosInstance.get(`${CHALLENGE_TYPES_URL}?isActive=true`)
+ const response = await axiosInstance.get(`${CHALLENGE_TYPES_URL}`)
return _.get(response, 'data', [])
}
@@ -54,7 +55,16 @@ 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', [])
+}
+
+/**
+ * Api request for fetching challenge timelines
+ * @returns {Promise<*>}
+ */
+export async function fetchChallengeTimelines () {
+ const response = await axiosInstance.get(`${CHALLENGE_TIMELINES_URL}?page=1&perPage=100`)
return _.get(response, 'data', [])
}
@@ -63,7 +73,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', [])
}
@@ -88,6 +98,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