diff --git a/.gitignore b/.gitignore index 7e0e01a7..53692ce2 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,8 @@ yarn-error.log* *.pem *.vscode *.pem +*.vscode + # e2e test case test-automation/temp test-automation/test-results \ No newline at end of file diff --git a/config/constants/development.js b/config/constants/development.js index 438ffea1..c9839840 100644 --- a/config/constants/development.js +++ b/config/constants/development.js @@ -14,11 +14,11 @@ module.exports = { MEMBER_API_URL: `${DEV_API_HOSTNAME}/v6/members`, CHALLENGE_API_URL: `${DEV_API_HOSTNAME}/v6/challenges`, CHALLENGE_API_VERSION: '1.1.0', - CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v6/timeline-templates`, - CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v6/challenge-types`, - CHALLENGE_TRACKS_URL: `${DEV_API_HOSTNAME}/v6/challenge-tracks`, - CHALLENGE_PHASES_URL: `${DEV_API_HOSTNAME}/v6/challenge-phases`, - CHALLENGE_TIMELINES_URL: `${DEV_API_HOSTNAME}/v6/challenge-timelines`, + CHALLENGE_TIMELINE_TEMPLATES_URL: `${DEV_API_HOSTNAME}/v5/timeline-templates`, + CHALLENGE_TYPES_URL: `${DEV_API_HOSTNAME}/v5/challenge-types`, + CHALLENGE_TRACKS_URL: `${DEV_API_HOSTNAME}/v5/challenge-tracks`, + 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}/v6/groups`, TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`, diff --git a/package-lock.json b/package-lock.json index a8070b92..09741239 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3194,9 +3194,9 @@ } }, "@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "requires": { "@types/connect": "*", "@types/node": "*" @@ -3237,13 +3237,12 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" }, "@types/express": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", - "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", + "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", "requires": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/qs": "*", "@types/serve-static": "*" } }, @@ -3257,9 +3256,9 @@ } }, "@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", + "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", "requires": { "@types/node": "*", "@types/qs": "*", @@ -3294,11 +3293,10 @@ } }, "@types/hoist-non-react-statics": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.6.tgz", - "integrity": "sha512-lPByRJUer/iN/xa4qpyL0qmL11DqNW81iU/IG1S3uvRUq4oKagz8VCxZjiWkumgt66YT3vOdDgZ0o32sGKtCEw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.7.tgz", + "integrity": "sha512-PQTyIulDkIDro8P+IHbKCsw7U2xxBYflVzW/FgWdCAePD9xGSidgA76/GeJ6lBKoblyhf9pBY763gbrN+1dI8g==", "requires": { - "@types/react": "*", "hoist-non-react-statics": "^3.3.0" }, "dependencies": { @@ -3361,9 +3359,9 @@ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, "@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==" }, "@types/q": { "version": "1.5.2", @@ -3371,43 +3369,28 @@ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, "@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==" }, "@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" }, - "@types/react": { - "version": "19.1.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.6.tgz", - "integrity": "sha512-JeG0rEWak0N6Itr6QUx+X60uQmN+5t3j9r/OVDtWzFXKaj6kD1BwJzOksD0FF6iWxZlbE1kB0q9vtnU2ekqa1Q==", - "requires": { - "csstype": "^3.0.2" - }, - "dependencies": { - "csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" - } - } - }, "@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "version": "0.17.5", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", + "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", "requires": { "@types/mime": "^1", "@types/node": "*" } }, "@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", + "version": "1.15.8", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", + "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", "requires": { "@types/http-errors": "*", "@types/node": "*", @@ -5529,12 +5512,12 @@ } }, "call-bound": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", - "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "requires": { - "call-bind-apply-helpers": "^1.0.1", - "get-intrinsic": "^1.2.6" + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" } }, "call-me-maybe": { @@ -5923,9 +5906,9 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, "codemirror": { - "version": "5.65.19", - "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.19.tgz", - "integrity": "sha512-+aFkvqhaAVr1gferNMuN8vkTSrWIFvzlMV9I2KBLCWS2WpZ2+UAkZjlMZmEuT+gcXTi6RrGQCkWq1/bDtGqhIA==" + "version": "5.65.20", + "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.20.tgz", + "integrity": "sha512-i5dLDDxwkFCbhjvL2pNjShsojoL3XHyDwsGv1jqETUoW+lzpBKKqNTUWgQwVAOa0tUm4BwekT455ujafi8payA==" }, "codemirror-spell-checker": { "version": "1.1.2", @@ -8937,13 +8920,14 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, @@ -8952,6 +8936,27 @@ "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" }, + "formidable": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", + "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", + "requires": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "dependencies": { + "qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "requires": { + "side-channel": "^1.1.0" + } + } + } + }, "formik": { "version": "2.4.6", "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz", @@ -11880,11 +11885,11 @@ "integrity": "sha512-Q8206k8pTY7krW32cdmPsP+DqqLgWx/hYPSj9/+7SYqSqz7UuwPbfSe07lQtvuuaVyiSJveXk0E5RydOuWwsEg==" }, "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "requires": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } @@ -11915,9 +11920,9 @@ } }, "follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" } } }, @@ -13150,15 +13155,15 @@ } }, "morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "requires": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "dependencies": { "debug": { @@ -13178,6 +13183,11 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "on-headers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==" } } }, @@ -13264,9 +13274,9 @@ "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" }, "nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==" }, "nanomatch": { "version": "1.2.13", @@ -20145,26 +20155,26 @@ "integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==" }, "serve-favicon": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.0.tgz", - "integrity": "sha512-FMW2RvqNr03x+C0WxTyu6sOv21oOjkq5j8tjquWccwa6ScNyGFOGJVpuS1NmTVGBAHS07xnSKotgf2ehQmf9iA==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", + "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", "requires": { "etag": "~1.8.1", - "fresh": "0.5.2", - "ms": "2.1.1", + "fresh": "~0.5.2", + "ms": "~2.1.3", "parseurl": "~1.3.2", - "safe-buffer": "5.1.1" + "safe-buffer": "~5.2.1" }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -21443,24 +21453,13 @@ }, "dependencies": { "debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { "ms": "^2.1.3" } }, - "formidable": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", - "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", - "requires": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0", - "qs": "^6.11.0" - } - }, "mime": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", @@ -21480,9 +21479,9 @@ } }, "semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" } } }, @@ -21998,8 +21997,8 @@ } }, "topcoder-react-lib": { - "version": "github:topcoder-platform/topcoder-react-lib#f728ef13f40ccbeac00ed0d1507997835e99058c", - "from": "github:topcoder-platform/topcoder-react-lib#1.2.18", + "version": "github:topcoder-platform/topcoder-react-lib#9ebc2542cf6b06281708e2650c405152c99cee27", + "from": "github:topcoder-platform/topcoder-react-lib#v6", "requires": { "@topcoder-platform/tc-auth-lib": "git+https://github.com/topcoder-platform/tc-auth-lib.git#1.0.4", "auth0-js": "^6.8.4", @@ -22018,7 +22017,7 @@ "redux-actions": "^2.4.0", "tc-core-library-js": "github:appirio-tech/tc-core-library-js#v2.6", "to-capital-case": "^1.0.0", - "topcoder-react-utils": "0.7.5" + "topcoder-react-utils": "github:topcoder-platform/topcoder-react-utils#v6" }, "dependencies": { "redux": { @@ -22035,9 +22034,8 @@ } }, "topcoder-react-utils": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/topcoder-react-utils/-/topcoder-react-utils-0.7.5.tgz", - "integrity": "sha512-/jolO/UUCC/FL/MniBMFi9d7Wc1KbzwvgT5STGs4T+7u7R26bQugGPpGVISEPuglsmW0Xybh6iRi+pT/muOkbg==", + "version": "github:topcoder-platform/topcoder-react-utils#0fcf9a756a371e0ad633636ba050a7881d862cb8", + "from": "github:topcoder-platform/topcoder-react-utils#v6", "requires": { "babel-runtime": "^6.26.0", "body-parser": "^1.18.3", diff --git a/package.json b/package.json index 428eff92..ef22e243 100644 --- a/package.json +++ b/package.json @@ -129,8 +129,7 @@ "build": "node scripts/build.js", "lint": "eslint ./src", "lint:fix": "eslint --fix ./src", - "test": "node scripts/test.js", - "restart": "node scripts/build.js && node server.js" + "test": "node scripts/test.js" }, "eslintConfig": { "extends": [ diff --git a/server.js b/server.js index b9d2ccae..d81a88a5 100644 --- a/server.js +++ b/server.js @@ -17,10 +17,10 @@ function check () { } app.use(healthCheck.middleware([check])) app.use((req, res, next) => { - //res.header('Referrer-Policy', 'strict-origin-when-cross-origin') + res.header('Referrer-Policy', 'strict-origin-when-cross-origin') res.header('Permissions-Policy', 'geolocation=(), microphone=(), camera=()') res.header('X-Content-Type-Options', 'nosniff') - // res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload') + res.header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload') res.header('Cache-control', 'public, max-age=0') res.header('Pragma', 'no-cache') res.setHeader('X-Frame-Options', 'DENY') diff --git a/src/actions/projects.js b/src/actions/projects.js index dfc6bf9b..89b1f6b5 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -52,7 +52,7 @@ function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { if (!isNaN(projectNameOrIdFilter)) { // if it is number filters['id'] = parseInt(projectNameOrIdFilter, 10) } else { // text search - filters['keyword'] = decodeURIComponent(projectNameOrIdFilter) + filters['keyword'] = `"${decodeURIComponent(projectNameOrIdFilter)}"` } } diff --git a/src/actions/users.js b/src/actions/users.js index 31a65ab2..d1c51ec4 100644 --- a/src/actions/users.js +++ b/src/actions/users.js @@ -82,7 +82,7 @@ export function searchUserProjects (isAdmin = true, keyword) { sort: 'updatedAt desc', perPage: 20, page: 1, - keyword + keyword: `"${keyword}"` } if (!isAdmin) { filters['memberOnly'] = true diff --git a/src/components/Buttons/OutlineButton/index.js b/src/components/Buttons/OutlineButton/index.js index 9d5f6914..5bc8140e 100644 --- a/src/components/Buttons/OutlineButton/index.js +++ b/src/components/Buttons/OutlineButton/index.js @@ -22,7 +22,7 @@ const OutlineButton = ({ type, text, link, onClick, url, className, submit, disa if (!_.isEmpty(link)) { return ( - + {text} ) @@ -38,7 +38,7 @@ const OutlineButton = ({ type, text, link, onClick, url, className, submit, disa OutlineButton.propTypes = { type: PropTypes.string.isRequired, text: PropTypes.string.isRequired, - link: PropTypes.string, + link: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), url: PropTypes.string, className: PropTypes.string, onClick: PropTypes.func, diff --git a/src/components/ChallengeEditor/ChallengeName-Field/index.js b/src/components/ChallengeEditor/ChallengeName-Field/index.js index 4b4feebb..470bb7e7 100644 --- a/src/components/ChallengeEditor/ChallengeName-Field/index.js +++ b/src/components/ChallengeEditor/ChallengeName-Field/index.js @@ -4,22 +4,47 @@ import styles from './ChallengeName-Field.module.scss' import cn from 'classnames' const ChallengeNameField = ({ challenge, onUpdateInput }) => { + const handleChange = (e) => { + // Remove any characters that are NOT letters, numbers, or spaces + const sanitizedValue = e.target.value.replace(/[^a-zA-Z0-9 ]/g, '') + onUpdateInput({ + target: { + name: e.target.name, + value: sanitizedValue + } + }) + } + return ( <>
- +
- +
- { challenge.submitTriggered && !challenge.name &&
-
-
- Work Name is required field + {challenge.submitTriggered && !challenge.name && ( +
+
+
+ Work Name is required field +
-
} + )} ) } diff --git a/src/components/ChallengeEditor/ChallengeViewTabs/index.js b/src/components/ChallengeEditor/ChallengeViewTabs/index.js index 6ff9f455..5968bb1e 100644 --- a/src/components/ChallengeEditor/ChallengeViewTabs/index.js +++ b/src/components/ChallengeEditor/ChallengeViewTabs/index.js @@ -210,7 +210,7 @@ const ChallengeViewTabs = ({ (
)} {canLaunch && (
- {challenge ? ( + {challenge.legacyId || isTask ? ( { let errMessage = 'Please set a copilot' const handleProperty = copilots.handle ? 'handle' : 'memberHandle' const selectedCopilot = _.find(copilots, { [handleProperty]: challenge.copilot }) const selectedCopilotHandle = selectedCopilot ? selectedCopilot[handleProperty] : undefined - const copilotFee = _.find(challenge.prizeSets, p => p.type === PRIZE_SETS_TYPE.COPILOT_PAYMENT, []) + const copilotFee = _.find(challenge.prizeSets, p => p.type === 'copilot', []) const selfService = challenge.selfService const copilotIsSelf = loggedInUser && selectedCopilotHandle === loggedInUser.handle const assignButtonText = `${selectedCopilot && copilotIsSelf ? 'Una' : 'A'}ssign Yourself` diff --git a/src/components/ChallengeEditor/index.js b/src/components/ChallengeEditor/index.js index bd1a43d6..e25f2208 100644 --- a/src/components/ChallengeEditor/index.js +++ b/src/components/ChallengeEditor/index.js @@ -960,7 +960,7 @@ class ChallengeEditor extends Component { return { ...p, prizes } }) challenge.status = status - if (status === CHALLENGE_STATUS.ACTIVE && isTask) { + if (status === 'Active' && isTask) { challenge.startDate = moment().format() } @@ -1028,7 +1028,7 @@ class ChallengeEditor extends Component { } const newChallenge = { - status: CHALLENGE_STATUS.NEW, + status: 'New', projectId: this.props.projectId, name, typeId, @@ -1100,7 +1100,7 @@ class ChallengeEditor extends Component { return ([ { name: `${challenge.name} Discussion`, - type: 'CHALLENGE', + type: 'challenge', provider: 'vanilla' } ]) @@ -1295,11 +1295,11 @@ class ChallengeEditor extends Component { } async onActiveChallenge () { - this.updateAllChallengeInfo(CHALLENGE_STATUS.ACTIVE) + this.updateAllChallengeInfo('Active') } async saveDraft () { - this.updateAllChallengeInfo(CHALLENGE_STATUS.DRAFT) + this.updateAllChallengeInfo('Draft') } async onlySave () { @@ -1492,7 +1492,7 @@ class ChallengeEditor extends Component { Closing Task Confirmation Modal and Error Modal */ if (isCloseTask && !isConfirm) { - const taskPrize = _.get(_.find(challenge.prizeSets, { type: PRIZE_SETS_TYPE.CHALLENGE_PRIZES }), 'prizes[0].value') + const taskPrize = _.get(_.find(challenge.prizeSets, { type: 'placement' }), 'prizes[0].value') const assignedMemberId = _.get(assignedMemberDetails, 'userId') const assignedMember = _.get(assignedMemberDetails, 'handle', `User Id: ${assignedMemberId}`) @@ -1540,7 +1540,7 @@ class ChallengeEditor extends Component { - {!this.state.hasValidationErrors ? ( + {(challenge.legacyId || isTask) && !this.state.hasValidationErrors ? ( ) : ( diff --git a/src/components/ChallengesComponent/ChallengeCard/index.js b/src/components/ChallengesComponent/ChallengeCard/index.js index 9947f48a..4347bc98 100644 --- a/src/components/ChallengesComponent/ChallengeCard/index.js +++ b/src/components/ChallengesComponent/ChallengeCard/index.js @@ -197,7 +197,7 @@ class ChallengeCard extends React.Component { this.setState({ isSaving: true }) const isTask = _.get(challenge, 'task.isTask', false) const payload = { - status: CHALLENGE_STATUS.ACTIVE + status: 'Active' } if (isTask) { payload.startDate = moment().format() diff --git a/src/components/ChallengesComponent/index.js b/src/components/ChallengesComponent/index.js index dd932641..af5a36ea 100644 --- a/src/components/ChallengesComponent/index.js +++ b/src/components/ChallengesComponent/index.js @@ -7,7 +7,7 @@ import PropTypes from 'prop-types' import { Helmet } from 'react-helmet' import { Link } from 'react-router-dom' import ProjectStatus from './ProjectStatus' -import { PROJECT_ROLES, PROJECT_STATUS, COPILOTS_URL } from '../../config/constants' +import { PROJECT_ROLES, PROJECT_STATUS, COPILOTS_URL, CHALLENGE_STATUS } from '../../config/constants' import { PrimaryButton, OutlineButton } from '../Buttons' import ChallengeList from './ChallengeList' import styles from './ChallengesComponent.module.scss' @@ -53,6 +53,12 @@ const ChallengesComponent = ({ const isReadOnly = checkReadOnlyRoles(auth.token) || loginUserRoleInProject === PROJECT_ROLES.READ const isAdminOrCopilot = checkAdminOrCopilot(auth.token, activeProject) + const projectStatus = activeProject && activeProject.status + ? activeProject.status.toUpperCase() + : '' + const isCompletedOrCancelled = + projectStatus === CHALLENGE_STATUS.CANCELLED || projectStatus === CHALLENGE_STATUS.COMPLETED + useEffect(() => { const loggedInUser = auth.user const projectMembers = activeProject.members @@ -85,6 +91,16 @@ const ChallengesComponent = ({
{activeProject && activeProject.id && !isReadOnly ? (
+ {isAdminOrCopilot && ( )} - {(checkAdmin(auth.token) || checkManager(auth.token)) && ( + {(checkAdmin(auth.token) || checkManager(auth.token)) && !isCompletedOrCancelled && ( )} diff --git a/src/components/Select/styles.js b/src/components/Select/styles.js index a2b44a38..7a0c05b1 100644 --- a/src/components/Select/styles.js +++ b/src/components/Select/styles.js @@ -55,6 +55,9 @@ export default { paddingRight: '6px', paddingLeft: '10px', border: 'none', + width: '100%', + display: 'grid', + gridTemplateColumns: '1fr', input: { width: '100% !important', height: 'auto !important', diff --git a/src/components/Users/Users.module.scss b/src/components/Users/Users.module.scss index 7451b0c6..862679f3 100644 --- a/src/components/Users/Users.module.scss +++ b/src/components/Users/Users.module.scss @@ -40,6 +40,11 @@ } } +.textContent { + font-size: 18px; + margin-top: 25px; +} + .row { display: flex; @@ -47,8 +52,6 @@ align-items: center; input { - max-width: 280px; - @include upto-sm { display: block; padding-bottom: 10px; @@ -400,7 +403,7 @@ justify-content: center; } -.addButtonContainer { +.addButtonContainer, .buttonWrapper { display: flex; justify-content: flex-start; height: 30px; @@ -408,14 +411,10 @@ margin-bottom: 20px; gap: 8px; > * { - width: 125px; + width: max-content; } } -.addUserContentContainer { - -} - .tcRadioButton { .tc-radioButton-label { @include roboto-light(); diff --git a/src/components/Users/index.js b/src/components/Users/index.js index 5ddc2e67..2164338b 100644 --- a/src/components/Users/index.js +++ b/src/components/Users/index.js @@ -44,6 +44,14 @@ class Users extends Component { this.debouncedOnInputChange = _.debounce(this.onInputChange, AUTOCOMPLETE_DEBOUNCE_TIME_MS) } + componentDidMount () { + if (this.props.initialProject && this.props.initialProject.id) { + this.setProjectOption({ + value: this.props.initialProject.id, + label: this.props.initialProject.name + }) + } + } setProjectOption (projectOption) { this.setState({ projectOption }) @@ -165,7 +173,8 @@ class Users extends Component { isLoadingProject } = this.props const { - searchKey + searchKey, + projectOption } = this.state const projectOptions = ((searchKey ? resultSearchUserProjects : projects) || []).map(p => { return { @@ -204,20 +213,28 @@ class Users extends Component {
- { - showAddUser && ( -
- this.onAddUserClick()} /> - this.onInviteUserClick()} /> -
- ) - } +
+ { + showAddUser && ( + <> + this.onAddUserClick()} /> + this.onInviteUserClick()} /> + + ) + } + {projectOption && ( + + )} +
{ this.state.showAddUserModal && ( ) } @@ -315,6 +335,10 @@ class Users extends Component { } Users.propTypes = { + initialProject: PropTypes.shape({ + id: PropTypes.string, + name: PropTypes.string + }), loadProject: PropTypes.func.isRequired, updateProjectMember: PropTypes.func.isRequired, removeProjectMember: PropTypes.func.isRequired, diff --git a/src/components/Users/user-add.modal.js b/src/components/Users/user-add.modal.js index 58b68d20..12a269e4 100644 --- a/src/components/Users/user-add.modal.js +++ b/src/components/Users/user-add.modal.js @@ -6,7 +6,7 @@ import Modal from '../Modal' import SelectUserAutocomplete from '../SelectUserAutocomplete' import { PROJECT_ROLES } from '../../config/constants' import PrimaryButton from '../Buttons/PrimaryButton' -import { addUserToProject, inviteUserToProject } from '../../services/projects' +import { addUserToProject, inviteUserToProject, updateProjectMemberRole } from '../../services/projects' import styles from './Users.module.scss' @@ -14,12 +14,22 @@ const theme = { container: styles.modalContainer } -const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, onClose }) => { +const UserAddModalContent = ({ + projectMembers, + projectOption, + projectId, + addNewProjectMember, + onMemberInvited, + onClose, + updateProjectMember +}) => { const [userToAdd, setUserToAdd] = useState(null) const [userPermissionToAdd, setUserPermissionToAdd] = useState(PROJECT_ROLES.READ) const [showSelectUserError, setShowSelectUserError] = useState(false) const [addUserError, setAddUserError] = useState(null) const [isAdding, setIsAdding] = useState(false) + const [isUserAddingFailed, setUserAddingFailed] = useState(false) + const [existingRole, setExistingRole] = useState('') const onUpdateUserToAdd = (option) => { if (option && option.value) { @@ -52,8 +62,12 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, }) if (failed) { const error = get(failed, '0.message', 'User cannot be invited') + const errorCode = get(failed, '0.error') + const role = get(failed, '0.role') setAddUserError(error) setIsAdding(false) + setUserAddingFailed(errorCode === 'ALREADY_MEMBER') + setExistingRole(role) } else if (rest.message) { setAddUserError(rest.message) setIsAdding(false) @@ -74,113 +88,141 @@ const UserAddModalContent = ({ projectId, addNewProjectMember, onMemberInvited, } } + const onConfirmCopilotRoleChange = async () => { + const member = projectMembers.find(item => item.userId === userToAdd.userId) + const action = member.role === 'manager' ? 'complete-copilot-requests' : '' + const response = await updateProjectMemberRole(projectId, member.id, 'copilot', action) + updateProjectMember(response) + onClose() + } + + const onCancelCopilotRoleChange = () => { + setUserAddingFailed(false) + setAddUserError('') + } + return ( -
-
Add User
-
-
-
- Member* : -
-
- + { + isUserAddingFailed && (['observer', 'customer', 'copilot', 'manager'].includes(existingRole)) && ( +
+
{`The copilot ${userToAdd.handle} is part of ${projectOption.label} project with ${existingRole} role.`}
+
+ +
- {showSelectUserError && ( -
-
Please select a member.
-
- )} -
-
- -
-
-
- setUserPermissionToAdd(PROJECT_ROLES.READ)} - /> - + ) + } + { + !isUserAddingFailed && ( +
+
Add User
+
+
+
+ Member* : +
+
+ +
-
-
-
- setUserPermissionToAdd(PROJECT_ROLES.WRITE)} - /> - + {showSelectUserError && ( +
+
Please select a member.
+
+ )} +
+
+ +
+
+
+ setUserPermissionToAdd(PROJECT_ROLES.READ)} + /> + +
+
+
+
+ setUserPermissionToAdd(PROJECT_ROLES.WRITE)} + /> + +
+
+
+
+ setUserPermissionToAdd(PROJECT_ROLES.MANAGER)} + /> + +
+
+
+
+ setUserPermissionToAdd(PROJECT_ROLES.COPILOT)} + /> + +
+
+ {addUserError && ( +
{addUserError}
+ )}
-
-
- setUserPermissionToAdd(PROJECT_ROLES.MANAGER)} +
+
+ -
-
-
-
- setUserPermissionToAdd(PROJECT_ROLES.COPILOT)} +
+ -
- {addUserError && ( -
{addUserError}
- )} -
-
-
- -
-
- -
-
-
+ ) + } ) } @@ -188,7 +230,10 @@ UserAddModalContent.propTypes = { projectId: PropTypes.number.isRequired, addNewProjectMember: PropTypes.func.isRequired, onMemberInvited: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired + onClose: PropTypes.func.isRequired, + projectOption: PropTypes.any.isRequired, + projectMembers: PropTypes.array.isRequired, + updateProjectMember: PropTypes.func.isRequired } export default UserAddModalContent diff --git a/src/config/constants.js b/src/config/constants.js index 8955269c..daa323e1 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -59,7 +59,7 @@ export const FILE_PICKER_PROGRESS_INTERVAL = 100 export const FILE_PICKER_UPLOAD_RETRY = 2 export const FILE_PICKER_UPLOAD_TIMEOUT = 30 * 60 * 1000 // 30 minutes export const SPECIFICATION_ATTACHMENTS_FOLDER = 'SPECIFICATION_ATTACHMENTS' -export const MEMBERS_API_URL = process.env.MEMBERS_API_URL +export const MEMBERS_API_URL = process.env.MEMBER_API_URL export const getAWSContainerFileURL = (key) => `https://${FILE_PICKER_CONTAINER_NAME}.s3.amazonaws.com/${key}` @@ -227,10 +227,10 @@ export const PHASE_STATUS = { // List of prize sets types export const PRIZE_SETS_TYPE = { - CHALLENGE_PRIZES: 'PLACEMENT', - COPILOT_PAYMENT: 'COPILOT', - REVIEWER_PAYMENT: 'REVIEWER', - CHECKPOINT_PRIZES: 'CHECKPOINT' + CHALLENGE_PRIZES: 'placement', + COPILOT_PAYMENT: 'copilot', + REVIEWER_PAYMENT: 'reviewer', + CHECKPOINT_PRIZES: 'checkpoint' } export const REVIEW_TYPES = { @@ -368,7 +368,7 @@ export const GROUPS_DROPDOWN_PER_PAGE = 1000000 // make sure we are getting all /** * The list of challenge types which can have multiple prizes */ -export const CHALLENGE_TYPES_WITH_MULTIPLE_PRIZES = ['CHALLENGE'] +export const CHALLENGE_TYPES_WITH_MULTIPLE_PRIZES = ['Challenge', 'Marathon Match'] /** * All the repeating messages. diff --git a/src/containers/ChallengeEditor/index.js b/src/containers/ChallengeEditor/index.js index e46dc711..f8dd590c 100644 --- a/src/containers/ChallengeEditor/index.js +++ b/src/containers/ChallengeEditor/index.js @@ -40,7 +40,7 @@ import { loadSubmissions } from '../../actions/challengeSubmissions' import { loadProject } from '../../actions/projects' import { connect } from 'react-redux' -import { SUBMITTER_ROLE_UUID, MESSAGE, PROJECT_ROLES, CHALLENGE_STATUS } from '../../config/constants' +import { SUBMITTER_ROLE_UUID, MESSAGE, PROJECT_ROLES } from '../../config/constants' import { patchChallenge } from '../../services/challenges' import ConfirmationModal from '../../components/Modal/ConfirmationModal' import AlertModal from '../../components/Modal/AlertModal' @@ -281,7 +281,7 @@ class ChallengeEditor extends Component { try { this.setState({ isLaunching: true }) const payload = { - status: CHALLENGE_STATUS.ACTIVE + status: 'Active' } if (isTask) { payload.startDate = moment().format() diff --git a/src/containers/ProjectInvitations/index.js b/src/containers/ProjectInvitations/index.js index 58e6f20d..fc9c4e49 100644 --- a/src/containers/ProjectInvitations/index.js +++ b/src/containers/ProjectInvitations/index.js @@ -46,16 +46,22 @@ const ProjectInvitations = ({ match, auth, isProjectLoading, history, projectDet const updateInvite = useCallback(async (status, source) => { setIsUpdating(status) - await updateProjectMemberInvite(projectId, invitation.id, status, source) - - // await for the project details to propagate - await delay(1000) - await loadProjectInvites(projectId) - toastr.success('Success', `Successfully ${status} the invitation.`) - - // await for the project details to fetch - await delay(1000) - history.push(status === PROJECT_MEMBER_INVITE_STATUS_ACCEPTED ? `/projects/${projectId}/challenges` : '/projects') + try { + await updateProjectMemberInvite(projectId, invitation.id, status, source) + + // await for the project details to propagate + await delay(1000) + await loadProjectInvites(projectId) + toastr.success('Success', `Successfully ${status} the invitation.`) + + // await for the project details to fetch + await delay(1000) + history.push(status === PROJECT_MEMBER_INVITE_STATUS_ACCEPTED ? `/projects/${projectId}/challenges` : '/projects') + } catch (e) { + toastr.error('Error', e.response.data.message) + await delay(1000) + history.push('/projects') + } }, [projectId, invitation, loadProjectInvites, history]) const acceptInvite = useCallback(() => updateInvite(PROJECT_MEMBER_INVITE_STATUS_ACCEPTED, source), [updateInvite, source]) diff --git a/src/containers/Projects/styles.module.scss b/src/containers/Projects/styles.module.scss index ef18461d..3fdceafb 100644 --- a/src/containers/Projects/styles.module.scss +++ b/src/containers/Projects/styles.module.scss @@ -51,6 +51,10 @@ border-radius: 3px; border: 1px solid $light-gray; background-color: $lighter-gray; + + > div { + width: 100%; + } } .tcCheckbox { diff --git a/src/containers/Users/index.js b/src/containers/Users/index.js index ab6b39f6..6f1cef67 100644 --- a/src/containers/Users/index.js +++ b/src/containers/Users/index.js @@ -2,6 +2,7 @@ import React, { Component } from 'react' import { connect } from 'react-redux' import _ from 'lodash' import PT from 'prop-types' +import { withRouter } from 'react-router-dom' import UsersComponent from '../../components/Users' import { PROJECT_ROLES } from '../../config/constants' import { fetchInviteMembers, fetchProjectById } from '../../services/projects' @@ -22,7 +23,11 @@ class Users extends Component { projectMembers: null, invitedMembers: null, isAdmin: false, - isLoadingProject: false + isLoadingProject: false, + project: props.location.state && props.location.state.projectId ? { + id: props.location.state && props.location.state.projectId, + name: props.location.state && props.location.state.projectName + } : null } this.loadProject = this.loadProject.bind(this) this.updateProjectMember = this.updateProjectMember.bind(this) @@ -33,7 +38,7 @@ class Users extends Component { } componentDidMount () { - const { token, isLoading, loadAllUserProjects, page } = this.props + const { token, isLoading, loadAllUserProjects, page, location } = this.props if (!isLoading) { const isAdmin = checkAdmin(token) const isManager = checkManager(token) @@ -44,6 +49,10 @@ class Users extends Component { this.setState({ isAdmin }) + + if (location.state && location.state.projectId) { + this.loadProject(location.state.projectId) + } } } @@ -157,6 +166,7 @@ class Users extends Component { isSearchingUserProjects } = this.props const { + project, projectMembers, invitedMembers, isAdmin, @@ -164,6 +174,7 @@ class Users extends Component { } = this.state return ( { } Users.propTypes = { + location: PT.object.isRequired, projects: PT.arrayOf(PT.object), resultSearchUserProjects: PT.arrayOf(PT.object), auth: PT.object, @@ -220,4 +232,4 @@ const mapDispatchToProps = { loadNextProjects } -export default connect(mapStateToProps, mapDispatchToProps)(Users) +export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Users)) diff --git a/src/services/projects.js b/src/services/projects.js index a3cf43c0..1a03221c 100644 --- a/src/services/projects.js +++ b/src/services/projects.js @@ -103,9 +103,10 @@ export async function fetchProjectPhases (id) { * @param newRole the new role * @returns {Promise<*>} */ -export async function updateProjectMemberRole (projectId, memberRecordId, newRole) { +export async function updateProjectMemberRole (projectId, memberRecordId, newRole, action) { const response = await axiosInstance.patch(`${PROJECTS_API_URL}/${projectId}/members/${memberRecordId}`, { - role: newRole + role: newRole, + action }) return _.get(response, 'data') }