diff --git a/.circleci/config.yml b/.circleci/config.yml index 7ef89ba5..43fa7b2e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,7 +152,7 @@ workflows: context: org-global filters: &filters-dev branches: - only: ["develop", "PM-803_wm-regression-fixes"] + only: ["develop", "PM-803_wm-regression-fixes", "PM-902_show-all-projects-on-challenge-page"] # Production builds are exectuted only on tagged commits to the # master branch. diff --git a/config/constants/development.js b/config/constants/development.js index a728f4ef..311336ed 100644 --- a/config/constants/development.js +++ b/config/constants/development.js @@ -25,6 +25,7 @@ module.exports = { RESOURCES_API_URL: `${DEV_API_HOSTNAME}/v5/resources`, RESOURCE_ROLES_API_URL: `${DEV_API_HOSTNAME}/v5/resource-roles`, SUBMISSIONS_API_URL: `${DEV_API_HOSTNAME}/v5/submissions`, + REVIEW_TYPE_API_URL: `${DEV_API_HOSTNAME}/v5/reviewTypes`, SUBMISSION_REVIEW_APP_URL: `https://submission-review.${DOMAIN}/challenges`, STUDIO_URL: `https://studio.${DOMAIN}`, CONNECT_APP_URL: `https://connect.${DOMAIN}`, diff --git a/config/constants/production.js b/config/constants/production.js index bbebe026..ff837ee2 100644 --- a/config/constants/production.js +++ b/config/constants/production.js @@ -24,6 +24,7 @@ module.exports = { RESOURCES_API_URL: `${PROD_API_HOSTNAME}/v5/resources`, RESOURCE_ROLES_API_URL: `${PROD_API_HOSTNAME}/v5/resource-roles`, SUBMISSIONS_API_URL: `${PROD_API_HOSTNAME}/v5/submissions`, + REVIEW_TYPE_API_URL: `${PROD_API_HOSTNAME}/v5/reviewTypes`, SUBMISSION_REVIEW_APP_URL: `https://submission-review.${DOMAIN}/challenges`, STUDIO_URL: `https://studio.${DOMAIN}`, CONNECT_APP_URL: `https://connect.${DOMAIN}`, diff --git a/package-lock.json b/package-lock.json index b7ab6daa..60545c25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3181,9 +3181,9 @@ } }, "@types/body-parser": { - "version": "1.19.2", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", - "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "requires": { "@types/connect": "*", "@types/node": "*" @@ -3198,9 +3198,9 @@ } }, "@types/connect": { - "version": "3.4.35", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", - "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "requires": { "@types/node": "*" } @@ -3224,12 +3224,12 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" }, "@types/express": { - "version": "4.17.17", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", - "integrity": "sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.0.tgz", + "integrity": "sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==", "requires": { "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", + "@types/express-serve-static-core": "^5.0.0", "@types/qs": "*", "@types/serve-static": "*" } @@ -3244,19 +3244,20 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.33", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.33.tgz", - "integrity": "sha512-TPBqmR/HRYI3eC2E5hmiivIzv+bidAfXofM+sbonAGvyDhySGw9/PQZFt2BLOrjUUR++4eJVpx6KnLQK1Fk9tA==", + "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==", "requires": { "@types/node": "*", "@types/qs": "*", - "@types/range-parser": "*" + "@types/range-parser": "*", + "@types/send": "*" } }, "@types/express-unless": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-2.0.1.tgz", - "integrity": "sha512-PJLiNw03EjkWDkQbhNjIXXDLObC3eMQhFASDV+WakFbT8eL7YdjlbV6MXd3Av5Lejq499d6pFuV1jyK+EHyG3Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-2.0.3.tgz", + "integrity": "sha512-iJbM7nsyBgnxCrCe7VjWIi4nyyhlaKUl7jxeHDpK+KXk3sYrUZViMkgFv9qSZmxDleB8dfpQR9gK5MGNyM/M6w==", "requires": { "express-unless": "*" } @@ -3298,6 +3299,11 @@ } } }, + "@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" + }, "@types/katex": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.11.1.tgz", @@ -3317,9 +3323,9 @@ } }, "@types/mime": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", - "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==" + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" }, "@types/minimatch": { "version": "3.0.3", @@ -3342,9 +3348,9 @@ "integrity": "sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw==" }, "@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" }, "@types/q": { "version": "1.5.2", @@ -3352,19 +3358,19 @@ "integrity": "sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==" }, "@types/qs": { - "version": "6.9.7", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", - "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + "version": "6.9.18", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", + "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==" }, "@types/range-parser": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", - "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + "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.0.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", - "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", "requires": { "csstype": "^3.0.2" }, @@ -3376,15 +3382,25 @@ } } }, - "@types/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "requires": { - "@types/mime": "*", + "@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==", + "requires": { + "@types/http-errors": "*", + "@types/node": "*", + "@types/send": "*" + } + }, "@types/tapable": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.2.tgz", @@ -5483,13 +5499,29 @@ "unset-value": "^1.0.0" } }, - "call-bind": { + "call-bind-apply-helpers": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "dependencies": { + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + } + } + }, + "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==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" } }, "call-me-maybe": { @@ -6106,9 +6138,9 @@ } }, "config": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/config/-/config-3.3.9.tgz", - "integrity": "sha512-G17nfe+cY7kR0wVpc49NCYvNtelm/pPy8czHoFkAgtV1lkmcp7DHtWCdDu+C9Z7gb2WVqa9Tm3uF9aKaPbCfhg==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/config/-/config-3.3.12.tgz", + "integrity": "sha512-Vmx389R/QVM3foxqBzXO8t2tUikYZP64Q6vQxGrsMpREeJc/aWRnPRERXWsYzOHAumx/AOoILWe6nU3ZJL+6Sw==", "requires": { "json5": "^2.2.3" }, @@ -6182,18 +6214,18 @@ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, "cookie-parser": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", - "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", "requires": { - "cookie": "0.4.1", + "cookie": "0.7.2", "cookie-signature": "1.0.6" }, "dependencies": { "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" } } }, @@ -6354,9 +6386,9 @@ } }, "crypto-js": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", - "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" }, "css-animation": { "version": "1.6.1", @@ -7092,6 +7124,15 @@ } } }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -7292,6 +7333,16 @@ "nan": "^2.14.0" } }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", @@ -7527,6 +7578,35 @@ "string.prototype.trimright": "^2.1.1" } }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -8844,12 +8924,13 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", "mime-types": "^2.1.12" } }, @@ -8859,9 +8940,25 @@ "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==" }, "formidable": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.6.tgz", - "integrity": "sha512-KcpbcpuLNOwrEjnbpMC0gS+X8ciDoZE1kkqzat4a8vrprf+s9pKNQ/QIwWfbfs4ltgmFl3MD177SNTkve3BwGQ==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "requires": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "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", @@ -9079,22 +9176,43 @@ "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "dependencies": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "dependencies": { + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" } } }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -9231,6 +9349,11 @@ "minimatch": "~3.0.2" } }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" + }, "graceful-fs": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", @@ -9379,6 +9502,21 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "requires": { + "has-symbols": "^1.0.3" + }, + "dependencies": { + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" + } + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -9457,6 +9595,21 @@ "minimalistic-assert": "^1.0.1" } }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + }, + "dependencies": { + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + } + } + }, "hast-to-hyperscript": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-9.0.1.tgz", @@ -9626,6 +9779,11 @@ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz", "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==" }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==" + }, "hide-powered-by": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", @@ -10224,12 +10382,12 @@ } }, "idtoken-verifier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/idtoken-verifier/-/idtoken-verifier-2.2.3.tgz", - "integrity": "sha512-hhpzB+MRgEvbwqzRLFdVbG55lKdXQVfeYEjAA2qu0UC72MSLeR0nX7P7rY5Dycz1aISHPOwq80hIPFoJ/+SItA==", + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/idtoken-verifier/-/idtoken-verifier-2.2.4.tgz", + "integrity": "sha512-5t7O8cNHpJBB8FnwLD0qFZqy/+qGICObQKUl0njD6vXKHhpZPLEe8LU7qv/GBWB3Qv5e/wAIFHYVi4SoQwdOxQ==", "requires": { "base64-js": "^1.5.1", - "crypto-js": "^4.1.1", + "crypto-js": "^4.2.0", "es6-promise": "^4.2.8", "jsbn": "^1.1.0", "unfetch": "^4.2.0", @@ -11770,9 +11928,9 @@ } }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" } } }, @@ -12158,22 +12316,26 @@ } }, "lru-memoizer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.2.0.tgz", - "integrity": "sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.3.0.tgz", + "integrity": "sha512-GXn7gyHAMhO13WSKrIiNfztwxodVsP8IoZ3XfrJV4yH2x0/OeTO/FIaAHTY5YekdGgW94njfuKmyyt1E0mR6Ug==", "requires": { "lodash.clonedeep": "^4.5.0", - "lru-cache": "~4.0.0" + "lru-cache": "6.0.0" }, "dependencies": { "lru-cache": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz", - "integrity": "sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "pseudomap": "^1.0.1", - "yallist": "^2.0.0" + "yallist": "^4.0.0" } + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -12239,6 +12401,11 @@ "repeat-string": "^1.0.0" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "math-random": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", @@ -13110,9 +13277,9 @@ "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" }, "nanoid": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==" }, "nanomatch": { "version": "1.2.13", @@ -20181,27 +20348,82 @@ "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==" }, "shortid": { - "version": "2.2.16", - "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", - "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", + "version": "2.2.17", + "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.17.tgz", + "integrity": "sha512-GpbM3gLF1UUXZvQw6MCyulHkWbRseNO4cyBEZresZRorwl1+SLu1ZdqgVtuwqz8mB6RpwPkm541mYSqrKyJSaA==", "requires": { - "nanoid": "^2.1.0" + "nanoid": "^3.3.8" } }, "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, "dependencies": { "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + } + } + }, + "side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "requires": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "dependencies": { + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + } + } + }, + "side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "dependencies": { + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" + } + } + }, + "side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "requires": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "dependencies": { + "object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==" } } }, @@ -21216,29 +21438,29 @@ } }, "superagent": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-5.3.1.tgz", - "integrity": "sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-7.1.6.tgz", + "integrity": "sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==", "requires": { "component-emitter": "^1.3.0", - "cookiejar": "^2.1.2", - "debug": "^4.1.1", - "fast-safe-stringify": "^2.0.7", - "form-data": "^3.0.0", - "formidable": "^1.2.2", + "cookiejar": "^2.1.3", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.0.1", "methods": "^1.1.2", - "mime": "^2.4.6", - "qs": "^6.9.4", + "mime": "2.6.0", + "qs": "^6.10.3", "readable-stream": "^3.6.0", - "semver": "^7.3.2" + "semver": "^7.3.7" }, "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "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==", "requires": { - "yallist": "^4.0.0" + "ms": "^2.1.3" } }, "mime": { @@ -21246,26 +21468,23 @@ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "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.0.4" + "side-channel": "^1.1.0" } }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" } } }, @@ -21402,15 +21621,16 @@ }, "dependencies": { "auth0-js": { - "version": "9.20.1", - "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.20.1.tgz", - "integrity": "sha512-m7k3O0Qs3Emr7cC2OPbbOp1duzgMzuTeESHgWK+FimGV6FjBX53dYtNIgQ49J7mkACeKje/Jlto9/6CO9YQhcQ==", + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/auth0-js/-/auth0-js-9.28.0.tgz", + "integrity": "sha512-2xIfQIGM0vX3IdPR91ztLO2+Ar2I5+3iFKcjuZO+LV9vRh4Wje+Ka1hnHjMU9dH892Lm3ZxBAHxRo68YToUhfg==", "requires": { "base64-js": "^1.5.1", - "idtoken-verifier": "^2.2.2", + "idtoken-verifier": "^2.2.4", "js-cookie": "^2.2.0", + "minimist": "^1.2.5", "qs": "^6.10.1", - "superagent": "^5.3.1", + "superagent": "^7.1.5", "url-join": "^4.0.1", "winchan": "^0.2.2" } @@ -21451,11 +21671,11 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "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.0.4" + "side-channel": "^1.1.0" } }, "winchan": { @@ -21780,9 +22000,8 @@ } }, "topcoder-react-lib": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/topcoder-react-lib/-/topcoder-react-lib-1.2.10.tgz", - "integrity": "sha512-Z8XICIvYbrciM+8vOJCRGiqEX4EzJ50pNApJnJyGfutjplmB1ns0m7/DezJJuB2mrb3A5hNki9KGV2EM+W7lOQ==", + "version": "github:topcoder-platform/topcoder-react-lib#f728ef13f40ccbeac00ed0d1507997835e99058c", + "from": "github:topcoder-platform/topcoder-react-lib#1.2.18", "requires": { "@topcoder-platform/tc-auth-lib": "git+https://github.com/topcoder-platform/tc-auth-lib.git#1.0.4", "auth0-js": "^6.8.4", diff --git a/package.json b/package.json index c0b81e02..8eab630d 100644 --- a/package.json +++ b/package.json @@ -115,7 +115,7 @@ "terser": "^3.16.1", "terser-webpack-plugin": "1.1.0", "topcoder-healthcheck-dropin": "^1.0.3", - "topcoder-react-lib": "^1.2.10", + "topcoder-react-lib": "github:topcoder-platform/topcoder-react-lib#1.2.18", "url-loader": "1.1.1", "webpack": "^4.43.0", "webpack-dev-server": "^3.11.0", diff --git a/src/actions/challenges.js b/src/actions/challenges.js index 7349d179..f1dabb7e 100644 --- a/src/actions/challenges.js +++ b/src/actions/challenges.js @@ -57,6 +57,7 @@ import { } from '../config/constants' import { loadProject } from './projects' import { removeChallengeFromPhaseProduct, saveChallengeAsPhaseProduct } from '../services/projects' +import { checkAdmin } from '../util/tc' /** * Member challenges related redux actions @@ -158,7 +159,7 @@ export function loadChallengesByPage ( filters['projectId'] = projectId } else if (_.isObject(projectId) && projectId.value > 0) { filters['projectId'] = projectId.value - } else if (userId) { + } else if (!checkAdmin(getState().auth.token) && userId) { // Note that we only add the memberId field if *no* project ID is given, // so that the list of *all challenges shows only those that the member is on filters['memberId'] = userId diff --git a/src/actions/projects.js b/src/actions/projects.js index 5f38015c..2d88e519 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -1,4 +1,12 @@ +import _ from 'lodash' + import { + PROJECT_TYPE_TAAS, + PROJECTS_PAGE_SIZE, + LOAD_PROJECTS_PENDING, + LOAD_PROJECTS_SUCCESS, + UNLOAD_PROJECTS_SUCCESS, + LOAD_PROJECTS_FAILURE, LOAD_PROJECT_BILLING_ACCOUNT, LOAD_CHALLENGE_MEMBERS_SUCCESS, LOAD_PROJECT_DETAILS, @@ -21,8 +29,96 @@ import { getProjectTypes, createProjectApi, fetchBillingAccounts, + fetchMemberProjects, updateProjectApi } from '../services/projects' +import { checkAdmin } from '../util/tc' + +function _loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { + return (dispatch, getState) => { + dispatch({ + type: LOAD_PROJECTS_PENDING + }) + + const filters = { + sort: 'lastActivityAt desc', + perPage: PROJECTS_PAGE_SIZE, + ...paramFilters + } + + if (!_.isEmpty(projectNameOrIdFilter)) { + if (!isNaN(projectNameOrIdFilter)) { // if it is number + filters['id'] = parseInt(projectNameOrIdFilter, 10) + } else { // text search + filters['keyword'] = decodeURIComponent(projectNameOrIdFilter) + } + } + + if (!checkAdmin(getState().auth.token)) { + filters['memberOnly'] = true + } + + // eslint-disable-next-line no-debugger + const state = getState().projects + fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({ + filters, + type: LOAD_PROJECTS_SUCCESS, + projects: _.uniqBy((filters.page ? state.projects || [] : []).concat(projects), 'id'), + total: pagination.xTotal, + page: pagination.xPage + })).catch(() => dispatch({ + type: LOAD_PROJECTS_FAILURE + })) + } +} + +export function loadProjects (projectNameOrIdFilter = '', paramFilters = {}) { + return async (dispatch, getState) => { + const _filters = _.assign({}, paramFilters) + if (_.isEmpty(_filters) || !_filters.type) { + let projectTypes = getState().projects.projectTypes + + if (!projectTypes.length) { + dispatch({ + type: LOAD_PROJECTS_PENDING + }) + await loadProjectTypes()(dispatch) + projectTypes = getState().projects.projectTypes + } + + _.assign(_filters, { + type: projectTypes.filter(d => d.key !== PROJECT_TYPE_TAAS).map(d => d.key) + }) + } + + return _loadProjects(projectNameOrIdFilter, _filters)(dispatch, getState) + } +} + +/** + * Load more projects for the authenticated user + */ +export function loadMoreProjects () { + return (dispatch, getState) => { + const { projectFilters, projectsPage } = getState().projects + + loadProjects('', _.assign({}, projectFilters, { + perPage: PROJECTS_PAGE_SIZE, + page: projectsPage + 1 + }))(dispatch, getState) + } +} + +/** + * Unloads projects of the authenticated user + */ +export function unloadProjects () { + return (dispatch) => { + dispatch({ + type: UNLOAD_PROJECTS_SUCCESS + }) + } +} /** * Loads project details diff --git a/src/actions/sidebar.js b/src/actions/sidebar.js index dd521d25..4fd02fd1 100644 --- a/src/actions/sidebar.js +++ b/src/actions/sidebar.js @@ -37,6 +37,7 @@ export function loadProjects (filterProjectName = '', paramFilters = {}) { }) const filters = { + status: 'active', sort: 'lastActivityAt desc', perPage: PROJECTS_PAGE_SIZE, ...paramFilters @@ -66,26 +67,18 @@ export function loadProjects (filterProjectName = '', paramFilters = {}) { } } -/** - * Load more projects for the authenticated user - */ -export function loadMoreProjects (filterProjectName = '', paramFilters = {}) { +// Load next page of projects +export function loadNextProjects () { return (dispatch, getState) => { - const state = getState().sidebar + const { projectFilters, projectsPage } = getState().sidebar - loadProjects(filterProjectName, _.assignIn({}, paramFilters, { + loadProjects('', _.assign({}, projectFilters, { perPage: PROJECTS_PAGE_SIZE, - page: state.page + 1 + page: projectsPage + 1 }))(dispatch, getState) } } -export function loadTaasProjects (filterProjectName = '', paramFilters = {}) { - return loadProjects(filterProjectName, Object.assign({ - type: 'talent-as-a-service' - }, paramFilters)) -} - /** * Unloads projects of the authenticated user */ diff --git a/src/assets/images/IconDownloadArtifacts.svg b/src/assets/images/IconDownloadArtifacts.svg new file mode 100644 index 00000000..ca1c58fe --- /dev/null +++ b/src/assets/images/IconDownloadArtifacts.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/IconReviewRatingList.svg b/src/assets/images/IconReviewRatingList.svg new file mode 100644 index 00000000..588f4972 --- /dev/null +++ b/src/assets/images/IconReviewRatingList.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/ChallengeEditor/ArtifactsListModal/ArtifactsListModal.module.scss b/src/components/ChallengeEditor/ArtifactsListModal/ArtifactsListModal.module.scss new file mode 100644 index 00000000..912b3731 --- /dev/null +++ b/src/components/ChallengeEditor/ArtifactsListModal/ArtifactsListModal.module.scss @@ -0,0 +1,53 @@ +@import "../../../styles/includes"; + +.container { + box-sizing: border-box; + background: $white; + opacity: 1; + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + border-radius: 6px; + margin: 0 auto; + width: 800px; + min-height: 350px; + padding-top: 60px; + .list { + .header { + border-bottom: 1px solid $tc-gray-60; + padding-bottom: 10px; + display: flex; + padding-left: 40px; + padding-right: 40px; + color: $tc-gray-70; + font-weight: 500; + + .header-title { + flex: 1; + } + } + .list-item { + border-bottom: 1px solid $tc-gray-60; + padding-bottom: 10px; + padding-top: 10px; + display: flex; + padding-left: 40px; + padding-right: 40px; + color: $tc-gray-70; + .artifact-name { + display: flex; + flex: 1; + } + .icon-download { + cursor: pointer; + } + } + .no-artifacts { + @include roboto; + + margin-top: 40px; + text-align: center; + } + } +} \ No newline at end of file diff --git a/src/components/ChallengeEditor/ArtifactsListModal/index.js b/src/components/ChallengeEditor/ArtifactsListModal/index.js new file mode 100644 index 00000000..2aa23b99 --- /dev/null +++ b/src/components/ChallengeEditor/ArtifactsListModal/index.js @@ -0,0 +1,115 @@ +import React, { useCallback, useEffect, useState } from 'react' +import Modal from '../../Modal' + +import styles from './ArtifactsListModal.module.scss' +import PropTypes from 'prop-types' +import ReactSVG from 'react-svg' +import { getTopcoderReactLib, isValidDownloadFile } from '../../../util/topcoder-react-lib' +import Loader from '../../Loader' +const assets = require.context('../../../assets/images', false, /svg/) + +export const ArtifactsListModal = ({ onClose, submissionId, token, theme }) => { + const [artifacts, setArtifacts] = useState([]) + const [loading, setLoading] = useState(false) + + const getArtifacts = useCallback(async () => { + const reactLib = getTopcoderReactLib() + const { getService } = reactLib.services.submissions + const submissionsService = getService(token) + const { artifacts: resp } = await submissionsService.getSubmissionArtifacts(submissionId) + setArtifacts(resp) + setLoading(false) + }, [submissionId, token]) + + const getExtensionFromMime = useCallback((mimeType) => { + const mimeMap = { + 'application/zip': 'zip', + 'application/pdf': 'pdf', + 'image/jpeg': 'jpg', + 'image/png': 'png', + 'text/plain': 'txt' + } + return mimeMap[mimeType] || 'zip' + }, []) + + useEffect(() => { + setLoading(true) + getArtifacts() + }, [submissionId]) + + const onDownloadArtifact = useCallback((item) => { + // download submission + const reactLib = getTopcoderReactLib() + const { getService } = reactLib.services.submissions + const submissionsService = getService(token) + submissionsService.downloadSubmissionArtifact(submissionId, item) + .then((blob) => { + isValidDownloadFile(blob).then((isValidFile) => { + if (isValidFile.success) { + // eslint-disable-next-line no-undef + const blobFile = new Blob([blob]) + const url = window.URL.createObjectURL(blobFile) + const link = document.createElement('a') + link.href = url + const extension = getExtensionFromMime(blob.type) + const fileName = `${submissionId}.${extension}` + link.setAttribute('download', `${fileName}`) + document.body.appendChild(link) + link.click() + link.parentNode.removeChild(link) + } else { + console.log('failed to download artifact') + } + }) + }) + }, [submissionId, token]) + + return ( + +
+
+
+
Artifact ID
+
Action
+
+ { + !loading && artifacts.map((item) => { + return ( +
+
{item}
+ onDownloadArtifact(item)} + /> +
+ ) + }) + } + + { + !loading && artifacts.length === 0 &&
No artifacts found
+ } + + { + loading && + } +
+
+
+ ) +} + +ArtifactsListModal.defaultProps = { + onClose: () => {}, + submissionId: '', + token: '', + theme: '' +} + +ArtifactsListModal.propTypes = { + onClose: PropTypes.func, + submissionId: PropTypes.string, + token: PropTypes.string, + theme: PropTypes.shape() +} diff --git a/src/components/ChallengeEditor/ChallengeView/index.js b/src/components/ChallengeEditor/ChallengeView/index.js index b0c3de96..430e22bf 100644 --- a/src/components/ChallengeEditor/ChallengeView/index.js +++ b/src/components/ChallengeEditor/ChallengeView/index.js @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react' import _ from 'lodash' import PropTypes from 'prop-types' import cn from 'classnames' -import { withRouter } from 'react-router-dom' +import { withRouter, Link } from 'react-router-dom' import styles from './ChallengeView.module.scss' import Track from '../../Track' import NDAField from '../NDAField' @@ -112,7 +112,10 @@ const ChallengeView = ({
- Project: {projectDetail ? projectDetail.name : ''} + Project: + {projectDetail ? projectDetail.name : ''} + +
{selectedMilestone && diff --git a/src/components/ChallengeEditor/RatingsListModal/RatingsListModal.module.scss b/src/components/ChallengeEditor/RatingsListModal/RatingsListModal.module.scss new file mode 100644 index 00000000..2a1c49e6 --- /dev/null +++ b/src/components/ChallengeEditor/RatingsListModal/RatingsListModal.module.scss @@ -0,0 +1,44 @@ +@import "../../../styles/includes"; + +.container { + box-sizing: border-box; + background: $white; + opacity: 1; + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-start; + border-radius: 6px; + margin: 0 auto; + width: 800px; + min-height: 350px; + padding-top: 60px; + .list { + .header { + border-bottom: 1px solid $tc-gray-60; + padding-bottom: 10px; + display: flex; + padding-left: 40px; + padding-right: 40px; + color: $tc-gray-70; + font-weight: 500; + + .header-item { + flex: 1; + } + } + .list-item { + border-bottom: 1px solid $tc-gray-60; + padding-bottom: 10px; + padding-top: 10px; + display: flex; + padding-left: 40px; + padding-right: 40px; + color: $tc-gray-70; + .list-col-item { + display: flex; + flex: 1; + } + } + } +} \ No newline at end of file diff --git a/src/components/ChallengeEditor/RatingsListModal/index.js b/src/components/ChallengeEditor/RatingsListModal/index.js new file mode 100644 index 00000000..33f17e94 --- /dev/null +++ b/src/components/ChallengeEditor/RatingsListModal/index.js @@ -0,0 +1,112 @@ +import React, { useCallback, useEffect, useState } from 'react' +import Modal from '../../Modal' + +import styles from './RatingsListModal.module.scss' +import PropTypes from 'prop-types' +import { getTopcoderReactLib } from '../../../util/topcoder-react-lib' +import Loader from '../../Loader' +import { getReviewTypes } from '../../../services/challenges' +import { SystemReviewers } from '../../../config/constants' + +export const RatingsListModal = ({ onClose, theme, token, submissionId, challengeId }) => { + const [reviews, setReviews] = useState([]) + const [loading, setLoading] = useState(false) + + const enrichSources = useCallback(async (submissionReviews, reviewSummation) => { + const reactLib = getTopcoderReactLib() + const { getService } = reactLib.services.members + const membersService = getService(token) + const resources = await membersService.getChallengeResources(challengeId) + const reviewTypes = await getReviewTypes() + + const finalReview = { + reviewType: 'Final score', + reviewer: '', + score: reviewSummation ? reviewSummation.aggregateScore : 'N/A', + isPassing: reviewSummation ? reviewSummation.isPassing : undefined + } + + return [...submissionReviews.map(review => { + const reviewType = reviewTypes.find(rt => rt.id === review.typeId) + const reviewer = resources.find(resource => resource.memberHandle === review.reviewerId) || SystemReviewers.Default + return { + ...review, + reviewType: reviewType ? reviewType.name : '', + reviewer + } + }), finalReview] + }, [token]) + + const getSubmission = useCallback(async () => { + const reactLib = getTopcoderReactLib() + const { getService } = reactLib.services.submissions + const submissionsService = getService(token) + const submissionInfo = await submissionsService.getSubmissionInformation(submissionId) + setReviews(await enrichSources(submissionInfo.review, submissionInfo.reviewSummation[0])) + setLoading(false) + }, [submissionId, token]) + + useEffect(() => { + setLoading(true) + getSubmission() + }, [submissionId]) + + return ( + +
+
+
+
Review Type
+
Reviewer
+
Score
+
Status
+
+ {reviews.map(review => { + const { isPassing } = review + const isFailed = isPassing === false + const isPassed = isPassing === true + const statusIsDefined = isPassed || isFailed + const status = isPassing ? 'Passed' : 'Failed' + + return ( +
+
+ {review.reviewType} +
+
+ {review.reviewer} +
+
+ {review.score} +
+
+ {statusIsDefined ? status : 'N/A'} +
+
+ ) + })} +
+ + { + loading && + } +
+
+ ) +} + +RatingsListModal.defaultProps = { + onClose: () => {}, + theme: '', + token: '', + submissionId: '', + challengeId: '' +} + +RatingsListModal.propTypes = { + onClose: PropTypes.func, + theme: PropTypes.shape(), + token: PropTypes.string, + submissionId: PropTypes.string, + challengeId: PropTypes.string +} diff --git a/src/components/ChallengeEditor/Submissions/Submissions.module.scss b/src/components/ChallengeEditor/Submissions/Submissions.module.scss index b741a9ba..e065b410 100644 --- a/src/components/ChallengeEditor/Submissions/Submissions.module.scss +++ b/src/components/ChallengeEditor/Submissions/Submissions.module.scss @@ -239,11 +239,24 @@ $base-unit: 5px; } .col-8Table { - button { + .button-wrapper { + display: flex; + align-items: center; + } + .download-submission-button { padding: 0; border: none; background-color: transparent; outline: none; + margin-right: 14px; + width: 24px; + svg { + width: 24px; + } + } + + .download-artifacts-button { + height: 40px; } } diff --git a/src/components/ChallengeEditor/Submissions/index.js b/src/components/ChallengeEditor/Submissions/index.js index 13428040..bfb29c91 100644 --- a/src/components/ChallengeEditor/Submissions/index.js +++ b/src/components/ChallengeEditor/Submissions/index.js @@ -29,10 +29,15 @@ import { } from '../../../util/files' import styles from './Submissions.module.scss' import modalStyles from '../../../styles/modal.module.scss' +import { ArtifactsListModal } from '../ArtifactsListModal' +import Tooltip from '../../Tooltip' +import { RatingsListModal } from '../RatingsListModal' const assets = require.context('../../../assets/images', false, /svg/) const ArrowDown = './arrow-down.svg' const Lock = './lock.svg' const Download = './IconSquareDownload.svg' +const DownloadArtifact = './IconDownloadArtifacts.svg' +const ReviewRatingList = './IconReviewRatingList.svg' const theme = { container: modalStyles.modalContainer @@ -50,7 +55,10 @@ class SubmissionsComponent extends React.Component { memberOfModal: '', sortedSubmissions: [], downloadingAll: false, - alertMessage: '' + alertMessage: '', + selectedSubmissionId: '', + showArtifactsListModal: false, + showRatingsListModal: false } this.getSubmissionsSortParam = this.getSubmissionsSortParam.bind(this) this.updateSortedSubmissions = this.updateSortedSubmissions.bind(this) @@ -59,6 +67,7 @@ class SubmissionsComponent extends React.Component { this.checkIsReviewPhaseComplete = this.checkIsReviewPhaseComplete.bind( this ) + this.downloadSubmission = this.downloadSubmission.bind(this) } componentDidMount () { @@ -220,6 +229,44 @@ class SubmissionsComponent extends React.Component { return isReviewPhaseComplete } + closeArtifactsModal () { + this.setState({ + selectedSubmissionId: '', + showArtifactsListModal: false + }) + } + + async downloadSubmission (submission) { + // download submission + const reactLib = getTopcoderReactLib() + const { getService } = reactLib.services.submissions + const submissionsService = getService(this.props.token) + submissionsService.downloadSubmission(submission.id) + .then((blob) => { + isValidDownloadFile(blob).then((isValidFile) => { + if (isValidFile.success) { + // eslint-disable-next-line no-undef + const url = window.URL.createObjectURL(new Blob([blob])) + const link = document.createElement('a') + link.href = url + let fileName = submission.legacySubmissionId + if (!fileName) { + fileName = submission.id + } + fileName = fileName + '.zip' + link.setAttribute('download', `${fileName}`) + document.body.appendChild(link) + link.click() + link.parentNode.removeChild(link) + } else { + this.setState({ + alertMessage: isValidFile.message || 'Can not download this submission.' + }) + } + }) + }) + } + render () { const { challenge, token, loggedInUserResource } = this.props const { checkpoints, track, type, tags } = challenge @@ -544,40 +591,38 @@ class SubmissionsComponent extends React.Component { {canDownloadSubmission ? ( - +
+ + + + + + + + + + + +
) : null} ) @@ -586,6 +631,36 @@ class SubmissionsComponent extends React.Component {
+ { + this.state.showArtifactsListModal ? ( + { + this.setState({ + selectedSubmissionId: '', + showArtifactsListModal: false + }) + }} + /> + ) : null + } + + { + this.state.showRatingsListModal ? ( + { + this.setState({ showRatingsListModal: false }) + }} + submissionId={this.state.selectedSubmissionId} + challengeId={this.props.challenge.id} + /> + ) : null + } + {canDownloadSubmission ? (
diff --git a/src/components/ChallengesComponent/ChallengeList/index.js b/src/components/ChallengesComponent/ChallengeList/index.js index 1d8baed8..71079d7d 100644 --- a/src/components/ChallengesComponent/ChallengeList/index.js +++ b/src/components/ChallengesComponent/ChallengeList/index.js @@ -341,14 +341,13 @@ class ChallengeList extends Component { const { activeProjectId, dashboard, - filterProjectOption, selfService, loadChallengesByPage } = this.props this.setState(_.cloneDeep(defaultSearchParam)) - let projectId = dashboard ? filterProjectOption : activeProjectId + let projectId = dashboard ? undefined : activeProjectId loadChallengesByPage( 1, @@ -402,7 +401,8 @@ class ChallengeList extends Component { isBillingAccountLoading, selfService, challengeTypes, - loginUserRoleInProject + loginUserRoleInProject, + fetchNextProjects } = this.props const isReadOnly = checkReadOnlyRoles(this.props.auth.token) || loginUserRoleInProject === PROJECT_ROLES.READ const isAdmin = checkAdmin(this.props.auth.token) @@ -507,6 +507,9 @@ class ChallengeList extends Component { setProjectStatus(e ? e.value : '')} + isClearable + /> +
+
+ + {projects.length > 0 ? ( + <> + + {projects && projects.length < projectsCount - 1 && ( + // fix + + )} + + ) : ( + No projects available yet + )} + + ) +} + +Projects.propTypes = { + projectsCount: PropTypes.number.isRequired, + projects: PropTypes.array, + auth: PropTypes.object.isRequired, + isLoading: PropTypes.bool.isRequired, + unloadProjects: PropTypes.func.isRequired, + loadProjects: PropTypes.func.isRequired, + loadMoreProjects: PropTypes.func.isRequired +} + +const mapStateToProps = ({ projects, auth }) => { + return { + projectsCount: projects.projectsCount, + projects: projects.projects, + isLoading: projects.isLoading, + auth + } +} + +const mapDispatchToProps = { + unloadProjects: unloadProjects, + loadProjects: loadProjects, + loadMoreProjects: loadMoreProjects +} + +export default withRouter( + connect(mapStateToProps, mapDispatchToProps)(Projects) +) diff --git a/src/containers/Projects/styles.module.scss b/src/containers/Projects/styles.module.scss new file mode 100644 index 00000000..d80341cc --- /dev/null +++ b/src/containers/Projects/styles.module.scss @@ -0,0 +1,54 @@ +@import '../../styles/includes'; + +.container { + padding: 20px 20px 0px; + + ul { + background-color: $lighter-gray; + list-style: none; + width: 100%; + padding: 0; + margin-top: 0; + border-radius: 3px; + flex: 1; + } + + div, + a { + color: $dark-gray; + text-decoration: none; + } +} + +.headerLine { + display: flex; + gap: 5px; + padding-bottom: 15px; + justify-content: space-between; + align-items: center; + .buttonNewProject { + min-width: 169px; + height: 40px; + text-decoration: none; + + :global { + span { + margin: 0 20px; + } + } + } +} + +.searchWrapper { + display: flex; + gap: 10px; + margin-bottom: 20px; + .searchInput { + width: 100%; + height: 40px; + padding: 0 10px; + border-radius: 3px; + border: 1px solid $light-gray; + background-color: $lighter-gray; + } +} \ No newline at end of file diff --git a/src/containers/TaaSList/index.js b/src/containers/TaaSList/index.js index 67610028..bdc58c50 100644 --- a/src/containers/TaaSList/index.js +++ b/src/containers/TaaSList/index.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect } from 'react' import { withRouter, Link } from 'react-router-dom' import { connect } from 'react-redux' import PropTypes from 'prop-types' @@ -7,15 +7,25 @@ import Loader from '../../components/Loader' import cn from 'classnames' import { checkAdmin, checkCopilot } from '../../util/tc' import { PrimaryButton } from '../../components/Buttons' +import InfiniteLoadTrigger from '../../components/InfiniteLoadTrigger' +import { loadProjects as _loadProjects, loadMoreProjects, unloadProjects as _unloadProjects } from '../../actions/projects' +import { PROJECT_TYPE_TAAS } from '../../config/constants' import styles from './styles.module.scss' -const TaaSList = ({ taasProjects, auth, isLoading }) => { +const TaaSList = ({ projects, auth, isLoading, projectsCount, loadProjects, loadMore, unloadProjects }) => { const isCopilot = checkCopilot(auth.token) const isAdmin = checkAdmin(auth.token) const canEdit = isCopilot || isAdmin - if (isLoading && taasProjects.length === 0) { + useEffect(() => { + loadProjects('', { type: PROJECT_TYPE_TAAS }) + }, []) + + // unload projects on dismount + useEffect(() => () => unloadProjects, []) + + if (isLoading && projects.length === 0) { return (
@@ -26,7 +36,7 @@ const TaaSList = ({ taasProjects, auth, isLoading }) => { return (
-
No project selected. Select one below
+

Projects

{(isCopilot || isAdmin) && ( { )}
- {taasProjects.length > 0 ? ( -
    - {taasProjects.map(p => ( -
  • - -
  • - ))} -
+ {projects.length > 0 ? ( + <> +
    + {projects.map(p => ( +
  • + +
  • + ))} +
+ {projects && projects.length < projectsCount - 1 && ( + // fix + + )} + ) : ( No TaaS projects available yet )} @@ -57,20 +73,29 @@ const TaaSList = ({ taasProjects, auth, isLoading }) => { } TaaSList.propTypes = { - taasProjects: PropTypes.array, + projectsCount: PropTypes.number.isRequired, + projects: PropTypes.array, auth: PropTypes.object.isRequired, - isLoading: PropTypes.bool.isRequired + isLoading: PropTypes.bool.isRequired, + loadProjects: PropTypes.func.isRequired, + unloadProjects: PropTypes.func.isRequired, + loadMore: PropTypes.func.isRequired } -const mapStateToProps = ({ sidebar, auth }) => { +const mapStateToProps = ({ projects, auth }) => { return { - taasProjects: sidebar.taasProjects, - isLoading: sidebar.isLoading, + projectsCount: projects.projectsCount, + projects: projects.projects, + isLoading: projects.isLoading, auth } } -const mapDispatchToProps = {} +const mapDispatchToProps = { + loadProjects: _loadProjects, + unloadProjects: _unloadProjects, + loadMore: loadMoreProjects +} export default withRouter( connect(mapStateToProps, mapDispatchToProps)(TaaSList) diff --git a/src/containers/Tab/index.js b/src/containers/Tab/index.js index d9845f37..22853e66 100644 --- a/src/containers/Tab/index.js +++ b/src/containers/Tab/index.js @@ -5,7 +5,6 @@ import { connect } from 'react-redux' import Tab from '../../components/Tab' import { loadProjects, - loadTaasProjects, setActiveProject, resetSidebarActiveParams, unloadProjects @@ -35,7 +34,7 @@ class TabContainer extends Component { !isLoading && !selfService && // do not fetch projects for users page - history.location.pathname !== '/users' + history.location.pathname === '/' ) { this.loadProjects(this.props) } @@ -46,7 +45,11 @@ class TabContainer extends Component { } componentWillReceiveProps (nextProps) { - const { projectId, isLoading, selfService, projects, isLoadProjectsSuccess } = nextProps + const { projectId, activeProjectId, isLoading, selfService, projects, isLoadProjectsSuccess } = nextProps + + if (projectId && activeProjectId < 0) { + this.props.setActiveProject(parseInt(projectId)) + } if (nextProps.history.location.pathname === '/') { this.setState({ currentTab: 1 }) @@ -96,9 +99,7 @@ class TabContainer extends Component { loadProjects (props) { const { history } = props - if (history.location.pathname === '/taas') { - this.props.loadTaasProjects() - } else { + if (history.location.pathname === '/') { this.props.loadProjects() } } @@ -139,7 +140,6 @@ TabContainer.propTypes = { isLoading: PropTypes.bool, isLoadProjectsSuccess: PropTypes.bool, loadProjects: PropTypes.func, - loadTaasProjects: PropTypes.func, unloadProjects: PropTypes.func, activeProjectId: PropTypes.number, history: PropTypes.any.isRequired, @@ -155,7 +155,6 @@ const mapStateToProps = ({ sidebar }) => ({ const mapDispatchToProps = { loadProjects, - loadTaasProjects, unloadProjects, setActiveProject, resetSidebarActiveParams diff --git a/src/reducers/projects.js b/src/reducers/projects.js index a2e9a938..e26c90d3 100644 --- a/src/reducers/projects.js +++ b/src/reducers/projects.js @@ -3,6 +3,10 @@ */ import _ from 'lodash' import { + LOAD_PROJECTS_PENDING, + LOAD_PROJECTS_SUCCESS, + LOAD_PROJECTS_FAILURE, + UNLOAD_PROJECTS_SUCCESS, LOAD_PROJECT_BILLING_ACCOUNTS_PENDING, LOAD_PROJECT_BILLING_ACCOUNTS_SUCCESS, LOAD_PROJECT_BILLING_ACCOUNTS_FAILURE, @@ -55,7 +59,7 @@ const dateFormat = 'MMM DD, YYYY' */ const buildBillingAccountOptions = (billingAccountObj) => { const billingAccountOptions = billingAccountObj.map(billingAccount => ({ - label: `(${billingAccount.tcBillingAccountId}) ${ + label: `[${billingAccount.tcBillingAccountId}] ${billingAccount.name} ${ billingAccount.endDate ? ' - ' + moment(billingAccount.endDate).format(dateFormat) : '' @@ -80,11 +84,44 @@ const initialState = { isPhasesLoading: false, phases: [], isProjectTypesLoading: false, - projectTypes: [] + projectFilters: {}, + projectTypes: [], + projects: [], + projectsCount: 0, + projectsPage: 0 } export default function (state = initialState, action) { switch (action.type) { + case LOAD_PROJECTS_PENDING: + return { ...state, isLoading: true } + case LOAD_PROJECTS_SUCCESS: + return { + ...state, + projectFilters: action.filters, + projects: action.projects, + projectsCount: action.total, + projectsPage: action.page, + isLoading: false + } + case UNLOAD_PROJECTS_SUCCESS: + return { + ...state, + isLoading: false, + projectFilters: {}, + projects: [], + projectsCount: 0, + projectsPage: 0 + } + case LOAD_PROJECTS_FAILURE: { + const errorMessage = _.get( + action.payload, + 'response.data.message', + 'Failed to load projects' + ) + toastFailure('Error', errorMessage) + return { ...state, isLoading: false } + } case LOAD_PROJECT_DETAILS_PENDING: return { ...state, isLoading: true } case LOAD_PROJECT_DETAILS_FAILURE: { diff --git a/src/reducers/sidebar.js b/src/reducers/sidebar.js index be25da00..5fac3a82 100644 --- a/src/reducers/sidebar.js +++ b/src/reducers/sidebar.js @@ -15,10 +15,10 @@ import { toastFailure } from '../util/toaster' const initialState = { activeProjectId: -1, isLoading: false, + projectFilters: {}, projects: [], - taasProjects: [], - total: 0, - page: 0, + projectsTotal: 0, + projectsPage: 0, isLoadProjectsSuccess: false } @@ -29,18 +29,15 @@ export default function (state = initialState, action) { case LOAD_PROJECTS_SUCCESS: return { ...state, - projects: _.filter(action.projects, p => p.type !== 'talent-as-a-service'), - taasProjects: _.filter(action.projects, { - type: 'talent-as-a-service' - }), - total: action.total, - page: action.page, + projects: action.projects, + projectsTotal: action.total, + projectsPage: action.page, isLoading: false, isLoggedIn: true, isLoadProjectsSuccess: true } case UNLOAD_PROJECTS_SUCCESS: - return { ...state, total: 0, page: 0, projects: [], isLoading: false, isLoggedIn: true, isLoadProjectsSuccess: false } + return { ...state, projectsTotal: 0, projectsPage: 0, projects: [], isLoading: false, isLoggedIn: true, isLoadProjectsSuccess: false } case LOAD_PROJECTS_PENDING: return { ...state, isLoading: true } case LOAD_PROJECTS_FAILURE: { diff --git a/src/routes.js b/src/routes.js index fee5af10..aa87a527 100644 --- a/src/routes.js +++ b/src/routes.js @@ -11,6 +11,7 @@ import TopBarContainer from './containers/TopbarContainer' import FooterContainer from './containers/FooterContainer' import Tab from './containers/Tab' import Challenges from './containers/Challenges' +import Projects from './containers/Projects' import TaaSList from './containers/TaaSList' import ProjectAssets from './containers/ProjectAssets' import TaaSProjectForm from './containers/TaaSProjectForm' @@ -195,7 +196,7 @@ class Routes extends React.Component { /> renderApp( - , + , , , diff --git a/src/services/challenges.js b/src/services/challenges.js index 0abab43d..f3c441c7 100644 --- a/src/services/challenges.js +++ b/src/services/challenges.js @@ -12,6 +12,7 @@ const { CHALLENGE_PHASES_URL, CHALLENGE_TIMELINES_URL, SUBMISSIONS_API_URL, + REVIEW_TYPE_API_URL, GROUPS_API_URL, TERMS_API_URL, RESOURCES_API_URL, @@ -267,6 +268,11 @@ export async function fetchSubmissions (challengeId) { return _.get(response, 'data', []) } +export async function getReviewTypes () { + const response = await axiosInstance.get(`${REVIEW_TYPE_API_URL}?perPage=500&page=1`) + return _.get(response, 'data', []) +} + /** * Api request for fetching resource roles * @returns {Promise<*>}