diff --git a/.circleci/config.yml b/.circleci/config.yml
index dfb7f6a9..e5ee2959 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -74,7 +74,7 @@ builddeploy_steps: &builddeploy_steps
source awsenvconf
./buildenv.sh -e $DEPLOY_ENV -b ${LOGICAL_ENV}-${APPNAME}-deployvar
source buildenvvar
- ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME}
+ ./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-global-appvar,${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME} -p FARGATE
#curl --request POST \
#--url https://circleci.com/api/v2/project/github/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME/pipeline \
#--header "Circle-Token: ${CIRCLE_TOKEN}" \
@@ -152,7 +152,7 @@ workflows:
context: org-global
filters: &filters-dev
branches:
- only: ["develop", "multiround", "release_0.20.9", "metadata-fix"]
+ only: ["develop", "PM-803_wm-regression-fixes", "PM-902_show-all-projects-on-challenge-page", "pm-1355_1"]
# Production builds are exectuted only on tagged commits to the
# master branch.
diff --git a/.github/workflows/code_reviewer.yml b/.github/workflows/code_reviewer.yml
new file mode 100644
index 00000000..1d313051
--- /dev/null
+++ b/.github/workflows/code_reviewer.yml
@@ -0,0 +1,22 @@
+name: AI PR Reviewer
+
+on:
+ pull_request:
+ types:
+ - opened
+ - synchronize
+permissions:
+ pull-requests: write
+jobs:
+ tc-ai-pr-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repo
+ uses: actions/checkout@v3
+
+ - name: TC AI PR Reviewer
+ uses: topcoder-platform/tc-ai-pr-reviewer@master
+ with:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # The GITHUB_TOKEN is there by default so you just need to keep it like it is and not necessarily need to add it as secret as it will throw an error. [More Details](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret)
+ LAB45_API_KEY: ${{ secrets.LAB45_API_KEY }}
+ exclude: "**/*.json, **/*.md, **/*.jpg, **/*.png, **/*.jpeg, **/*.bmp, **/*.webp" # Optional: exclude patterns separated by commas
diff --git a/config/constants/development.js b/config/constants/development.js
index fd7938dc..e418af3c 100644
--- a/config/constants/development.js
+++ b/config/constants/development.js
@@ -19,12 +19,15 @@ module.exports = {
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`,
+ COPILOTS_URL: `https://copilots.${DOMAIN}`,
PROJECT_API_URL: `${DEV_API_HOSTNAME}/v5/projects`,
GROUPS_API_URL: `${DEV_API_HOSTNAME}/v5/groups`,
TERMS_API_URL: `${DEV_API_HOSTNAME}/v5/terms`,
+ MEMBERS_API_URL: `${DEV_API_HOSTNAME}/v5/members`,
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}`,
@@ -44,8 +47,10 @@ module.exports = {
CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c', 'ecd58c69-238f-43a4-a4bb-d172719b9f31', '78b37a69-92d5-4ad7-bf85-c79b65420c79', '929bc408-9cf2-4b3e-ba71-adfbf693046c'],
FILE_PICKER_API_KEY: process.env.FILE_PICKER_API_KEY,
FILE_PICKER_CONTAINER_NAME: 'tc-challenge-v5-dev',
+ FILE_PICKER_SUBMISSION_CONTAINER_NAME: process.env.FILE_PICKER_SUBMISSION_CONTAINER_NAME || 'submission-staging-dev',
FILE_PICKER_REGION: 'us-east-1',
FILE_PICKER_CNAME: 'fs.topcoder.com',
+ FILE_PICKER_LOCATION: 's3',
// if idle for this many minutes, show user a prompt saying they'll be logged out
IDLE_TIMEOUT_MINUTES: 10,
// duration to show the prompt saying user will be logged out, before actually logging out the user
@@ -57,5 +62,5 @@ module.exports = {
SKILLS_V5_API_URL: `${API_V5}/standardized-skills/skills/autocomplete`,
UPDATE_SKILLS_V5_API_URL: `${API_V5}/standardized-skills/challenge-skills`,
SALESFORCE_BILLING_ACCOUNT_LINK: 'https://c.cs18.visual.force.com/apex/baredirect?id=',
- TYPEFORM_URL: 'https://topcoder.typeform.com/to/YJ7AL4p8'
+ PROFILE_URL: 'https://profiles.topcoder-dev.com/'
}
diff --git a/config/constants/production.js b/config/constants/production.js
index 56a94c63..882ee5be 100644
--- a/config/constants/production.js
+++ b/config/constants/production.js
@@ -18,12 +18,15 @@ module.exports = {
CHALLENGE_TRACKS_URL: `${PROD_API_HOSTNAME}/v5/challenge-tracks`,
CHALLENGE_PHASES_URL: `${PROD_API_HOSTNAME}/v5/challenge-phases`,
CHALLENGE_TIMELINES_URL: `${PROD_API_HOSTNAME}/v5/challenge-timelines`,
+ COPILOTS_URL: `https://copilots.${DOMAIN}`,
PROJECT_API_URL: `${PROD_API_HOSTNAME}/v5/projects`,
GROUPS_API_URL: `${PROD_API_HOSTNAME}/v5/groups`,
TERMS_API_URL: `${PROD_API_HOSTNAME}/v5/terms`,
+ MEMBERS_API_URL: `${PROD_API_HOSTNAME}/v5/members`,
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}`,
@@ -43,8 +46,10 @@ module.exports = {
CREATE_FORUM_TYPE_IDS: ['927abff4-7af9-4145-8ba1-577c16e64e2e', 'dc876fa4-ef2d-4eee-b701-b555fcc6544c', 'ecd58c69-238f-43a4-a4bb-d172719b9f31', '78b37a69-92d5-4ad7-bf85-c79b65420c79', '929bc408-9cf2-4b3e-ba71-adfbf693046c'],
FILE_PICKER_API_KEY: process.env.FILE_PICKER_API_KEY,
FILE_PICKER_CONTAINER_NAME: 'tc-challenge-v5-prod',
+ FILE_PICKER_SUBMISSION_CONTAINER_NAME: process.env.FILE_PICKER_SUBMISSION_CONTAINER_NAME || 'submission-staging-prod',
FILE_PICKER_REGION: 'us-east-1',
FILE_PICKER_CNAME: 'fs.topcoder.com',
+ FILE_PICKER_LOCATION: 's3',
IDLE_TIMEOUT_MINUTES: 10,
IDLE_TIMEOUT_GRACE_MINUTES: 5,
MULTI_ROUND_CHALLENGE_TEMPLATE_ID: 'd4201ca4-8437-4d63-9957-3f7708184b07',
@@ -54,5 +59,5 @@ module.exports = {
SKILLS_V5_API_URL: `${API_V5}/standardized-skills/skills/autocomplete`,
UPDATE_SKILLS_V5_API_URL: `${API_V5}/standardized-skills/challenge-skills`,
SALESFORCE_BILLING_ACCOUNT_LINK: 'https://topcoder.my.salesforce.com/apex/baredirect?id=',
- TYPEFORM_URL: 'https://topcoder.typeform.com/to/YJ7AL4p8'
+ PROFILE_URL: 'https://profiles.topcoder.com/'
}
diff --git a/package-lock.json b/package-lock.json
index 26029628..60545c25 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2931,6 +2931,11 @@
"prop-types": "^15.7.2"
}
},
+ "@hookform/resolvers": {
+ "version": "3.10.0",
+ "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
+ "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag=="
+ },
"@jridgewell/gen-mapping": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
@@ -3137,6 +3142,25 @@
"resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-1.2.1.tgz",
"integrity": "sha512-mA5Q5ulPoGQ+e08Vts1R6xw2QU0BKEnMH/KcqoYoS7Gk6imvMTpyFPeu1g+NOZObSIoAzA3/kRzY8m96cEBA2A=="
},
+ "@toast-ui/editor": {
+ "version": "2.5.4",
+ "resolved": "https://registry.npmjs.org/@toast-ui/editor/-/editor-2.5.4.tgz",
+ "integrity": "sha512-XsuYlPQxhec9dHQREFAigjE4enHSuGMF7D0YQ6wW7phmusvAu0FnJfZUPjJBoU/GKz7WP5U6fKU9/P+8j65D8A==",
+ "requires": {
+ "@types/codemirror": "0.0.71",
+ "codemirror": "^5.48.4"
+ },
+ "dependencies": {
+ "@types/codemirror": {
+ "version": "0.0.71",
+ "resolved": "https://registry.npmjs.org/@types/codemirror/-/codemirror-0.0.71.tgz",
+ "integrity": "sha512-b2oEEnno1LIGKMR7uBEsr40al1UijF1HEpRn0+Yf1xOLl24iQgB7DBpZVMM7y54G5wCNoclDrRO65E6KHPNO2w==",
+ "requires": {
+ "@types/tern": "*"
+ }
+ }
+ }
+ },
"@tootallnate/once": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
@@ -3157,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": "*"
@@ -3174,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": "*"
}
@@ -3200,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": "*"
}
@@ -3220,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": "*"
}
@@ -3255,6 +3280,30 @@
"@types/unist": "*"
}
},
+ "@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==",
+ "requires": {
+ "@types/react": "*",
+ "hoist-non-react-statics": "^3.3.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ }
+ }
+ },
+ "@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",
@@ -3274,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",
@@ -3299,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",
@@ -3309,24 +3358,49 @@
"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/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/react": {
+ "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"
+ },
+ "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==",
"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",
@@ -5425,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": {
@@ -5819,6 +5909,11 @@
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
+ "codemirror": {
+ "version": "5.65.18",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz",
+ "integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA=="
+ },
"codemirror-spell-checker": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/codemirror-spell-checker/-/codemirror-spell-checker-1.1.2.tgz",
@@ -6043,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"
},
@@ -6119,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=="
}
}
},
@@ -6291,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",
@@ -6821,6 +6916,11 @@
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
},
+ "deepmerge": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+ "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
+ },
"default-gateway": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
@@ -7024,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",
@@ -7224,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",
@@ -7459,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",
@@ -8776,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"
}
},
@@ -8791,9 +8940,60 @@
"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",
+ "resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz",
+ "integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==",
+ "requires": {
+ "@types/hoist-non-react-statics": "^3.3.1",
+ "deepmerge": "^2.1.1",
+ "hoist-non-react-statics": "^3.3.0",
+ "lodash": "^4.17.21",
+ "lodash-es": "^4.17.21",
+ "react-fast-compare": "^2.0.1",
+ "tiny-warning": "^1.0.2",
+ "tslib": "^2.0.0"
+ },
+ "dependencies": {
+ "hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "requires": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+ },
+ "tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
+ }
+ }
},
"forwarded": {
"version": "0.1.2",
@@ -8976,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",
@@ -9128,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",
@@ -9276,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",
@@ -9354,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",
@@ -9523,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",
@@ -10121,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",
@@ -11667,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=="
}
}
},
@@ -12055,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=="
}
}
},
@@ -12136,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",
@@ -13007,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",
@@ -17495,6 +17765,11 @@
"react-is": "^16.8.1"
}
},
+ "property-expr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.6.tgz",
+ "integrity": "sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA=="
+ },
"property-information": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.6.0.tgz",
@@ -20073,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": {
+ "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.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-bind": "^1.0.0",
- "get-intrinsic": "^1.0.2",
- "object-inspect": "^1.9.0"
+ "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.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=="
}
}
},
@@ -21108,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": {
@@ -21138,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=="
}
}
},
@@ -21294,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"
}
@@ -21343,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": {
@@ -21553,6 +21881,11 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "tiny-case": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz",
+ "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q=="
+ },
"tiny-invariant": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz",
@@ -21667,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",
@@ -21799,6 +22131,11 @@
}
}
},
+ "toposort": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz",
+ "integrity": "sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg=="
+ },
"tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
@@ -21914,6 +22251,11 @@
"prelude-ls": "~1.1.2"
}
},
+ "type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA=="
+ },
"type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -24272,6 +24614,17 @@
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
},
+ "yup": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/yup/-/yup-1.6.1.tgz",
+ "integrity": "sha512-JED8pB50qbA4FOkDol0bYF/p60qSEDQqBD0/qeIrUCG1KbPBIQ776fCUNb9ldbPcSTxA69g/47XTo4TqWiuXOA==",
+ "requires": {
+ "property-expr": "^2.0.5",
+ "tiny-case": "^1.0.3",
+ "toposort": "^2.0.2",
+ "type-fest": "^2.19.0"
+ }
+ },
"zwitch": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz",
diff --git a/package.json b/package.json
index dbd288db..8eab630d 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"@fortawesome/fontawesome-svg-core": "^1.2.14",
"@fortawesome/free-solid-svg-icons": "^5.7.1",
"@fortawesome/react-fontawesome": "^0.1.4",
+ "@hookform/resolvers": "^3.10.0",
"@nateradebaugh/react-datetime": "^4.4.11",
"@popperjs/core": "^2.5.4",
"@svgr/webpack": "2.4.1",
@@ -114,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/public/static/comment.jpg b/public/static/comment.jpg
new file mode 100644
index 00000000..52a7ce58
Binary files /dev/null and b/public/static/comment.jpg differ
diff --git a/public/static/logo.jpg b/public/static/logo.jpg
new file mode 100644
index 00000000..44cf1a5f
Binary files /dev/null and b/public/static/logo.jpg differ
diff --git a/src/actions/challengeSubmissions.js b/src/actions/challengeSubmissions.js
index b060c19f..c2122209 100644
--- a/src/actions/challengeSubmissions.js
+++ b/src/actions/challengeSubmissions.js
@@ -2,12 +2,12 @@ import { fetchSubmissions } from '../services/challenges'
import { LOAD_CHALLENGE_SUBMISSIONS } from '../config/constants'
-export function loadSubmissions (challengeId) {
- return dispatch => {
+export function loadSubmissions (challengeId, page) {
+ return (dispatch) => {
if (challengeId) {
dispatch({
type: LOAD_CHALLENGE_SUBMISSIONS,
- payload: fetchSubmissions(challengeId)
+ payload: fetchSubmissions(challengeId, page)
})
}
}
diff --git a/src/actions/challenges.js b/src/actions/challenges.js
index 7349d179..60eb631f 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, checkManager } from '../util/tc'
/**
* Member challenges related redux actions
@@ -158,7 +159,11 @@ 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) &&
+ !checkManager(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 45d18a58..dfc6bf9b 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,
@@ -9,7 +17,11 @@ import {
LOAD_PROJECT_BILLING_ACCOUNTS,
UPDATE_PROJECT_PENDING,
UPDATE_PROJECT_SUCCESS,
- UPDATE_PROJECT_FAILURE
+ UPDATE_PROJECT_FAILURE,
+ ADD_PROJECT_ATTACHMENT_SUCCESS,
+ UPDATE_PROJECT_ATTACHMENT_SUCCESS,
+ REMOVE_PROJECT_ATTACHMENT_SUCCESS,
+ LOAD_PROJECT_INVITES
} from '../config/constants'
import {
fetchProjectById,
@@ -18,8 +30,97 @@ import {
getProjectTypes,
createProjectApi,
fetchBillingAccounts,
- updateProjectApi
+ fetchMemberProjects,
+ updateProjectApi,
+ getProjectInvites
} from '../services/projects'
+import { checkAdmin, checkManager } 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) && !checkManager(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
@@ -72,6 +173,18 @@ export function loadProjectTypes () {
}
}
+/**
+ * Loads project invites
+ */
+export function loadProjectInvites (projectId) {
+ return (dispatch) => {
+ return dispatch({
+ type: LOAD_PROJECT_INVITES,
+ payload: getProjectInvites(projectId)
+ })
+ }
+}
+
/**
* Creates a project
*/
@@ -84,6 +197,45 @@ export function createProject (project) {
}
}
+/**
+ * Add attachment to project
+ * @param {Object} newAttachment new attachment data
+ */
+export function addAttachment (newAttachment) {
+ return (dispatch) => {
+ return dispatch({
+ type: ADD_PROJECT_ATTACHMENT_SUCCESS,
+ payload: newAttachment
+ })
+ }
+}
+
+/**
+ * Update project attachment
+ * @param {Object} newAttachment new attachment data
+ */
+export function updateAttachment (newAttachment) {
+ return (dispatch) => {
+ return dispatch({
+ type: UPDATE_PROJECT_ATTACHMENT_SUCCESS,
+ payload: newAttachment
+ })
+ }
+}
+
+/**
+ * Remove project attachment
+ * @param {number} attachmentId attachment id
+ */
+export function removeAttachment (attachmentId) {
+ return (dispatch) => {
+ return dispatch({
+ type: REMOVE_PROJECT_ATTACHMENT_SUCCESS,
+ payload: attachmentId
+ })
+ }
+}
+
/**
* Only loads project details
* @param {String} projectId Id of the project
diff --git a/src/actions/sidebar.js b/src/actions/sidebar.js
index 4a843747..0c02d8f4 100644
--- a/src/actions/sidebar.js
+++ b/src/actions/sidebar.js
@@ -8,8 +8,10 @@ import {
LOAD_PROJECTS_PENDING,
LOAD_PROJECTS_SUCCESS,
RESET_SIDEBAR_ACTIVE_PARAMS,
- UNLOAD_PROJECTS_SUCCESS
+ UNLOAD_PROJECTS_SUCCESS,
+ PROJECTS_PAGE_SIZE
} from '../config/constants'
+import { checkAdmin, checkManager } from '../util/tc'
import _ from 'lodash'
/**
@@ -28,8 +30,8 @@ export function setActiveProject (projectId) {
/**
* Loads projects of the authenticated user
*/
-export function loadProjects (filterProjectName = '', myProjects = true, paramFilters = {}) {
- return (dispatch) => {
+export function loadProjects (filterProjectName = '', paramFilters = {}) {
+ return (dispatch, getState) => {
dispatch({
type: LOAD_PROJECTS_PENDING
})
@@ -37,6 +39,7 @@ export function loadProjects (filterProjectName = '', myProjects = true, paramFi
const filters = {
status: 'active',
sort: 'lastActivityAt desc',
+ perPage: PROJECTS_PAGE_SIZE,
...paramFilters
}
if (!_.isEmpty(filterProjectName)) {
@@ -47,21 +50,35 @@ export function loadProjects (filterProjectName = '', myProjects = true, paramFi
}
}
- // filters['perPage'] = 20
- // filters['page'] = 1
- if (myProjects) {
+ if (!checkAdmin(getState().auth.token) && !checkManager(getState().auth.token)) {
filters['memberOnly'] = true
}
- fetchMemberProjects(filters).then(projects => dispatch({
+ // eslint-disable-next-line no-debugger
+ const state = getState().sidebar
+ fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({
type: LOAD_PROJECTS_SUCCESS,
- projects
+ projects: _.uniqBy((state.projects || []).concat(projects), 'id'),
+ total: pagination.xTotal,
+ page: pagination.xPage
})).catch(() => dispatch({
type: LOAD_PROJECTS_FAILURE
}))
}
}
+// Load next page of projects
+export function loadNextProjects () {
+ return (dispatch, getState) => {
+ const { projectFilters, projectsPage } = getState().sidebar
+
+ loadProjects('', _.assign({}, projectFilters, {
+ perPage: PROJECTS_PAGE_SIZE,
+ page: projectsPage + 1
+ }))(dispatch, getState)
+ }
+}
+
/**
* Unloads projects of the authenticated user
*/
diff --git a/src/actions/users.js b/src/actions/users.js
index 92e54cc1..31a65ab2 100644
--- a/src/actions/users.js
+++ b/src/actions/users.js
@@ -10,33 +10,55 @@ import {
SEARCH_USER_PROJECTS_SUCCESS,
SEARCH_USER_PROJECTS_FAILURE
} from '../config/constants'
+import _ from 'lodash'
/**
* Loads projects of the authenticated user
*/
-export function loadAllUserProjects (isAdmin = true) {
- return (dispatch) => {
+export function loadAllUserProjects (params, isAdmin = true, isManager = true) {
+ return (dispatch, getState) => {
dispatch({
type: LOAD_ALL_USER_PROJECTS_PENDING
})
+ const state = getState().users
+
const filters = {
status: 'active',
- sort: 'lastActivityAt desc'
+ sort: 'lastActivityAt desc',
+ perPage: 20,
+ ...params
}
- if (!isAdmin) {
+
+ if (!isAdmin && !isManager) {
filters['memberOnly'] = true
}
- fetchMemberProjects(filters).then(projects => dispatch({
+ fetchMemberProjects(filters).then(({ projects, pagination }) => dispatch({
type: LOAD_ALL_USER_PROJECTS_SUCCESS,
- projects
+ projects: _.uniqBy((filters.page ? state.allUserProjects || [] : []).concat(projects), 'id'),
+ total: pagination.xTotal,
+ page: pagination.xPage
})).catch(() => dispatch({
type: LOAD_ALL_USER_PROJECTS_FAILURE
}))
}
}
+export function loadNextProjects (isAdmin = true, isManager = true) {
+ return (dispatch, getState) => {
+ const { page, total, allUserProjects } = getState().users
+ if (allUserProjects.length >= total) {
+ return
+ }
+
+ loadAllUserProjects(_.assign({}, {
+ perPage: 20,
+ page: page + 1
+ }), isAdmin, isManager)(dispatch, getState)
+ }
+}
+
/**
* Filter projects of the authenticated user
*
@@ -66,7 +88,7 @@ export function searchUserProjects (isAdmin = true, keyword) {
filters['memberOnly'] = true
}
- fetchMemberProjects(filters).then(projects => dispatch({
+ fetchMemberProjects(filters).then(({ projects }) => dispatch({
type: SEARCH_USER_PROJECTS_SUCCESS,
projects
})).catch(() => dispatch({
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/assets/images/files/aac.svg b/src/assets/images/files/aac.svg
new file mode 100644
index 00000000..38d95bbf
--- /dev/null
+++ b/src/assets/images/files/aac.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/ai.svg b/src/assets/images/files/ai.svg
new file mode 100644
index 00000000..33d19427
--- /dev/null
+++ b/src/assets/images/files/ai.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/ase.svg b/src/assets/images/files/ase.svg
new file mode 100644
index 00000000..ad573707
--- /dev/null
+++ b/src/assets/images/files/ase.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/asp.svg b/src/assets/images/files/asp.svg
new file mode 100644
index 00000000..0a254722
--- /dev/null
+++ b/src/assets/images/files/asp.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/aspx.svg b/src/assets/images/files/aspx.svg
new file mode 100644
index 00000000..de4dd224
--- /dev/null
+++ b/src/assets/images/files/aspx.svg
@@ -0,0 +1,22 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/avi.svg b/src/assets/images/files/avi.svg
new file mode 100644
index 00000000..9da8a8c3
--- /dev/null
+++ b/src/assets/images/files/avi.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/bmp.svg b/src/assets/images/files/bmp.svg
new file mode 100644
index 00000000..d966f646
--- /dev/null
+++ b/src/assets/images/files/bmp.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/c++.svg b/src/assets/images/files/c++.svg
new file mode 100644
index 00000000..bbe352c4
--- /dev/null
+++ b/src/assets/images/files/c++.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/cad.svg b/src/assets/images/files/cad.svg
new file mode 100644
index 00000000..16ee1953
--- /dev/null
+++ b/src/assets/images/files/cad.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/cfm.svg b/src/assets/images/files/cfm.svg
new file mode 100644
index 00000000..79870942
--- /dev/null
+++ b/src/assets/images/files/cfm.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/cgi.svg b/src/assets/images/files/cgi.svg
new file mode 100644
index 00000000..e8811aa6
--- /dev/null
+++ b/src/assets/images/files/cgi.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/csh.svg b/src/assets/images/files/csh.svg
new file mode 100644
index 00000000..eb5cca5d
--- /dev/null
+++ b/src/assets/images/files/csh.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/css.svg b/src/assets/images/files/css.svg
new file mode 100644
index 00000000..59692551
--- /dev/null
+++ b/src/assets/images/files/css.svg
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/csv.svg b/src/assets/images/files/csv.svg
new file mode 100644
index 00000000..e1dd6611
--- /dev/null
+++ b/src/assets/images/files/csv.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/default.svg b/src/assets/images/files/default.svg
new file mode 100644
index 00000000..2676714d
--- /dev/null
+++ b/src/assets/images/files/default.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/dmg.svg b/src/assets/images/files/dmg.svg
new file mode 100644
index 00000000..1e55afb8
--- /dev/null
+++ b/src/assets/images/files/dmg.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/doc.svg b/src/assets/images/files/doc.svg
new file mode 100644
index 00000000..dee20735
--- /dev/null
+++ b/src/assets/images/files/doc.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/docx.svg b/src/assets/images/files/docx.svg
new file mode 100644
index 00000000..298c8c0e
--- /dev/null
+++ b/src/assets/images/files/docx.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/eps.svg b/src/assets/images/files/eps.svg
new file mode 100644
index 00000000..35879b7a
--- /dev/null
+++ b/src/assets/images/files/eps.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/epub.svg b/src/assets/images/files/epub.svg
new file mode 100644
index 00000000..7e18a411
--- /dev/null
+++ b/src/assets/images/files/epub.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/exe.svg b/src/assets/images/files/exe.svg
new file mode 100644
index 00000000..6d9238cc
--- /dev/null
+++ b/src/assets/images/files/exe.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/flash.svg b/src/assets/images/files/flash.svg
new file mode 100644
index 00000000..7ead3253
--- /dev/null
+++ b/src/assets/images/files/flash.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/flv.svg b/src/assets/images/files/flv.svg
new file mode 100644
index 00000000..375ae6d4
--- /dev/null
+++ b/src/assets/images/files/flv.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/font.svg b/src/assets/images/files/font.svg
new file mode 100644
index 00000000..841cf5a9
--- /dev/null
+++ b/src/assets/images/files/font.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/gif.svg b/src/assets/images/files/gif.svg
new file mode 100644
index 00000000..eefd4a67
--- /dev/null
+++ b/src/assets/images/files/gif.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/gpx.svg b/src/assets/images/files/gpx.svg
new file mode 100644
index 00000000..1052c35c
--- /dev/null
+++ b/src/assets/images/files/gpx.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/gzip.svg b/src/assets/images/files/gzip.svg
new file mode 100644
index 00000000..3547af13
--- /dev/null
+++ b/src/assets/images/files/gzip.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/html.svg b/src/assets/images/files/html.svg
new file mode 100644
index 00000000..5400dc37
--- /dev/null
+++ b/src/assets/images/files/html.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/ics.svg b/src/assets/images/files/ics.svg
new file mode 100644
index 00000000..c5bc30f8
--- /dev/null
+++ b/src/assets/images/files/ics.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/iso.svg b/src/assets/images/files/iso.svg
new file mode 100644
index 00000000..a3154bd5
--- /dev/null
+++ b/src/assets/images/files/iso.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/jar.svg b/src/assets/images/files/jar.svg
new file mode 100644
index 00000000..73320066
--- /dev/null
+++ b/src/assets/images/files/jar.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/java.svg b/src/assets/images/files/java.svg
new file mode 100644
index 00000000..b4e93e49
--- /dev/null
+++ b/src/assets/images/files/java.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/jpg.svg b/src/assets/images/files/jpg.svg
new file mode 100644
index 00000000..d2c71c95
--- /dev/null
+++ b/src/assets/images/files/jpg.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/js.svg b/src/assets/images/files/js.svg
new file mode 100644
index 00000000..d3830b5c
--- /dev/null
+++ b/src/assets/images/files/js.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/jsp.svg b/src/assets/images/files/jsp.svg
new file mode 100644
index 00000000..7c73e070
--- /dev/null
+++ b/src/assets/images/files/jsp.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/link-12.svg b/src/assets/images/files/link-12.svg
new file mode 100644
index 00000000..c7a68910
--- /dev/null
+++ b/src/assets/images/files/link-12.svg
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/src/assets/images/files/log.svg b/src/assets/images/files/log.svg
new file mode 100644
index 00000000..ddef4c49
--- /dev/null
+++ b/src/assets/images/files/log.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/max.svg b/src/assets/images/files/max.svg
new file mode 100644
index 00000000..cda62f32
--- /dev/null
+++ b/src/assets/images/files/max.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/md.svg b/src/assets/images/files/md.svg
new file mode 100644
index 00000000..d94d0e8e
--- /dev/null
+++ b/src/assets/images/files/md.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/mkv.svg b/src/assets/images/files/mkv.svg
new file mode 100644
index 00000000..78ac9de0
--- /dev/null
+++ b/src/assets/images/files/mkv.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/mov.svg b/src/assets/images/files/mov.svg
new file mode 100644
index 00000000..63fdcceb
--- /dev/null
+++ b/src/assets/images/files/mov.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/mp3.svg b/src/assets/images/files/mp3.svg
new file mode 100644
index 00000000..cd0004d7
--- /dev/null
+++ b/src/assets/images/files/mp3.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/mp4.svg b/src/assets/images/files/mp4.svg
new file mode 100644
index 00000000..07efa4fb
--- /dev/null
+++ b/src/assets/images/files/mp4.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/mpg.svg b/src/assets/images/files/mpg.svg
new file mode 100644
index 00000000..8f0063bf
--- /dev/null
+++ b/src/assets/images/files/mpg.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/obj.svg b/src/assets/images/files/obj.svg
new file mode 100644
index 00000000..b10608e5
--- /dev/null
+++ b/src/assets/images/files/obj.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/otf.svg b/src/assets/images/files/otf.svg
new file mode 100644
index 00000000..ec9e8a84
--- /dev/null
+++ b/src/assets/images/files/otf.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/pdf.svg b/src/assets/images/files/pdf.svg
new file mode 100644
index 00000000..e51db6af
--- /dev/null
+++ b/src/assets/images/files/pdf.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/php.svg b/src/assets/images/files/php.svg
new file mode 100644
index 00000000..4fa31bf5
--- /dev/null
+++ b/src/assets/images/files/php.svg
@@ -0,0 +1,13 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/png.svg b/src/assets/images/files/png.svg
new file mode 100644
index 00000000..dc79b7b7
--- /dev/null
+++ b/src/assets/images/files/png.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/pptx.svg b/src/assets/images/files/pptx.svg
new file mode 100644
index 00000000..d6b420ee
--- /dev/null
+++ b/src/assets/images/files/pptx.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/psd.svg b/src/assets/images/files/psd.svg
new file mode 100644
index 00000000..c9505839
--- /dev/null
+++ b/src/assets/images/files/psd.svg
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/py.svg b/src/assets/images/files/py.svg
new file mode 100644
index 00000000..7ad94154
--- /dev/null
+++ b/src/assets/images/files/py.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/rar.svg b/src/assets/images/files/rar.svg
new file mode 100644
index 00000000..c11a7ea1
--- /dev/null
+++ b/src/assets/images/files/rar.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/raw.svg b/src/assets/images/files/raw.svg
new file mode 100644
index 00000000..f636dace
--- /dev/null
+++ b/src/assets/images/files/raw.svg
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/rb.svg b/src/assets/images/files/rb.svg
new file mode 100644
index 00000000..4bec9d8d
--- /dev/null
+++ b/src/assets/images/files/rb.svg
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/rss.svg b/src/assets/images/files/rss.svg
new file mode 100644
index 00000000..ee5e4420
--- /dev/null
+++ b/src/assets/images/files/rss.svg
@@ -0,0 +1,25 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/rtf.svg b/src/assets/images/files/rtf.svg
new file mode 100644
index 00000000..d6833570
--- /dev/null
+++ b/src/assets/images/files/rtf.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/sketch.svg b/src/assets/images/files/sketch.svg
new file mode 100644
index 00000000..ebe192e7
--- /dev/null
+++ b/src/assets/images/files/sketch.svg
@@ -0,0 +1,24 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/sql.svg b/src/assets/images/files/sql.svg
new file mode 100644
index 00000000..75b8f3bc
--- /dev/null
+++ b/src/assets/images/files/sql.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/srt.svg b/src/assets/images/files/srt.svg
new file mode 100644
index 00000000..ab7e8658
--- /dev/null
+++ b/src/assets/images/files/srt.svg
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/svg.svg b/src/assets/images/files/svg.svg
new file mode 100644
index 00000000..9923ee36
--- /dev/null
+++ b/src/assets/images/files/svg.svg
@@ -0,0 +1,21 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/tif.svg b/src/assets/images/files/tif.svg
new file mode 100644
index 00000000..ca24a164
--- /dev/null
+++ b/src/assets/images/files/tif.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/tiff.svg b/src/assets/images/files/tiff.svg
new file mode 100644
index 00000000..928ce965
--- /dev/null
+++ b/src/assets/images/files/tiff.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/ttf.svg b/src/assets/images/files/ttf.svg
new file mode 100644
index 00000000..33800659
--- /dev/null
+++ b/src/assets/images/files/ttf.svg
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/txt.svg b/src/assets/images/files/txt.svg
new file mode 100644
index 00000000..992f5d5e
--- /dev/null
+++ b/src/assets/images/files/txt.svg
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/wav.svg b/src/assets/images/files/wav.svg
new file mode 100644
index 00000000..989a380a
--- /dev/null
+++ b/src/assets/images/files/wav.svg
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/xlsx.svg b/src/assets/images/files/xlsx.svg
new file mode 100644
index 00000000..1555d7b3
--- /dev/null
+++ b/src/assets/images/files/xlsx.svg
@@ -0,0 +1,19 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/xml.svg b/src/assets/images/files/xml.svg
new file mode 100644
index 00000000..f57f8428
--- /dev/null
+++ b/src/assets/images/files/xml.svg
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src/assets/images/files/zip.svg b/src/assets/images/files/zip.svg
new file mode 100644
index 00000000..ed8c4a95
--- /dev/null
+++ b/src/assets/images/files/zip.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/src/components/AssetsLibrary/DownloadFile/index.js b/src/components/AssetsLibrary/DownloadFile/index.js
new file mode 100644
index 00000000..a76a95ba
--- /dev/null
+++ b/src/components/AssetsLibrary/DownloadFile/index.js
@@ -0,0 +1,63 @@
+/* Component to render button to download project attachment file */
+
+import React, { useState } from 'react'
+import PropTypes from 'prop-types'
+import _ from 'lodash'
+import styles from './styles.module.scss'
+import Loader from '../../Loader'
+import { toastr } from 'react-redux-toastr'
+import cn from 'classnames'
+import { getProjectAttachment } from '../../../services/projects'
+import ReactSVG from 'react-svg'
+const Download = './IconSquareDownload.svg'
+const assets = require.context('../../../assets/images', false, /svg/)
+
+const DownloadFile = ({ classsName, file, projectId }) => {
+ const [isLoading, setIsLoading] = useState(false)
+
+ return (
+
+ )
+}
+
+DownloadFile.defaultProps = {
+ file: {}
+}
+
+DownloadFile.propTypes = {
+ classsName: PropTypes.string,
+ projectId: PropTypes.string,
+ file: PropTypes.shape()
+}
+
+export default DownloadFile
diff --git a/src/components/AssetsLibrary/DownloadFile/styles.module.scss b/src/components/AssetsLibrary/DownloadFile/styles.module.scss
new file mode 100644
index 00000000..fccd9fd5
--- /dev/null
+++ b/src/components/AssetsLibrary/DownloadFile/styles.module.scss
@@ -0,0 +1,35 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ padding: 0;
+ margin: 0;
+ border: none;
+ outline: none !important;
+ background-color: transparent;
+ color: $tc-gray-90 !important;
+ &:hover .downloadIcon {
+ opacity: 1;
+ }
+}
+
+.loader {
+ width: auto;
+ height: auto;
+ margin-left: 5px;
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+}
+
+.downloadIcon {
+ opacity: 0;
+ margin-left: 5px;
+ transition: 0.15s ease;
+ svg {
+ width: 12px;
+ height: 12px;
+ }
+}
\ No newline at end of file
diff --git a/src/components/AssetsLibrary/ModalAddLink/index.js b/src/components/AssetsLibrary/ModalAddLink/index.js
new file mode 100644
index 00000000..420f287a
--- /dev/null
+++ b/src/components/AssetsLibrary/ModalAddLink/index.js
@@ -0,0 +1,178 @@
+/* Component to render add link modal */
+
+import React, { useMemo, useState, useEffect, useCallback } from 'react'
+import { useForm } from 'react-hook-form'
+import { yupResolver } from '@hookform/resolvers/yup'
+import { connect } from 'react-redux'
+import _ from 'lodash'
+import PropTypes from 'prop-types'
+import styles from './styles.module.scss'
+import cn from 'classnames'
+import { toastr } from 'react-redux-toastr'
+import Modal from '../../Modal'
+import PrimaryButton from '../../Buttons/PrimaryButton'
+import OutlineButton from '../../Buttons/OutlineButton'
+import FieldLabelDynamic from '../../FieldLabelDynamic'
+import FieldInput from '../../FieldInput'
+import { assetsLibraryAddLinkSchema } from '../../../util/validation'
+import { addAttachment, updateAttachment } from '../../../actions/projects'
+import {
+ addProjectAttachmentApi,
+ updateProjectAttachmentApi
+} from '../../../services/projects'
+import { ATTACHMENT_TYPE_LINK } from '../../../config/constants'
+
+const ModalAddLink = ({
+ classsName,
+ theme,
+ onCancel,
+ link,
+ addAttachment,
+ updateAttachment,
+ projectId
+}) => {
+ const [isProcessing, setIsProcessing] = useState(false)
+ const buttonText = useMemo(() => (link ? 'Edit Link' : 'Add Link'), [link])
+
+ const {
+ register,
+ handleSubmit,
+ reset,
+ formState: { errors, isValid, isDirty }
+ } = useForm({
+ defaultValues: {
+ title: '',
+ path: ''
+ },
+ resolver: yupResolver(assetsLibraryAddLinkSchema),
+ mode: 'all'
+ })
+
+ useEffect(() => {
+ if (link) {
+ reset({
+ title: link.title,
+ path: link.path
+ })
+ }
+ }, [link])
+
+ const onSubmit = useCallback(
+ data => {
+ if (!link) {
+ setIsProcessing(true)
+ addProjectAttachmentApi(projectId, {
+ ...data,
+ tags: [],
+ type: ATTACHMENT_TYPE_LINK
+ })
+ .then(result => {
+ toastr.success('Success', 'Added link to the project successfully.')
+ setIsProcessing(false)
+ addAttachment(result)
+ onCancel()
+ })
+ .catch(e => {
+ setIsProcessing(false)
+ const errorMessage = _.get(
+ e,
+ 'response.data.message',
+ 'Failed to add link.'
+ )
+ toastr.error('Error', errorMessage)
+ })
+ } else {
+ setIsProcessing(true)
+ updateProjectAttachmentApi(projectId, link.id, data)
+ .then(result => {
+ toastr.success('Success', 'Updated link successfully.')
+ setIsProcessing(false)
+ updateAttachment(result)
+ onCancel()
+ })
+ .catch(e => {
+ setIsProcessing(false)
+ const errorMessage = _.get(
+ e,
+ 'response.data.message',
+ 'Failed to update link.'
+ )
+ toastr.error('Error', errorMessage)
+ })
+ }
+ },
+ [link, projectId]
+ )
+
+ return (
+
+
+
{link ? 'EDIT LINK' : 'ADD A LINK'}
+
+
+
+
+ )
+}
+
+ModalAddLink.defaultProps = {
+ isProcessing: false,
+ projectId: '',
+ onCancel: () => {}
+}
+
+ModalAddLink.propTypes = {
+ classsName: PropTypes.string,
+ theme: PropTypes.shape(),
+ onCancel: PropTypes.func,
+ addAttachment: PropTypes.func.isRequired,
+ updateAttachment: PropTypes.func.isRequired,
+ link: PropTypes.shape(),
+ projectId: PropTypes.string
+}
+
+const mapStateToProps = () => {
+ return {}
+}
+
+const mapDispatchToProps = {
+ addAttachment,
+ updateAttachment
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ModalAddLink)
diff --git a/src/components/AssetsLibrary/ModalAddLink/styles.module.scss b/src/components/AssetsLibrary/ModalAddLink/styles.module.scss
new file mode 100644
index 00000000..8ff2a99d
--- /dev/null
+++ b/src/components/AssetsLibrary/ModalAddLink/styles.module.scss
@@ -0,0 +1,51 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ box-sizing: border-box;
+ background: $white;
+ opacity: 1;
+ position: relative;
+ border-radius: 6px;
+ margin: 0 auto;
+ padding: 15px 20px;
+ width: 600px;
+ max-width: 95vw;
+}
+
+.button {
+ height: 40px;
+
+ span,
+ button {
+ padding: 0 20px;
+ }
+}
+
+.title {
+ @include roboto-medium();
+
+ color: grey;
+ font-size: 16px;
+}
+
+.blockRow {
+ width: 100%;
+}
+
+.blockForm {
+ display: flex;
+ flex-direction: column;
+ gap: 30px;
+ align-items: center;
+ margin-top: 30px;
+ margin-bottom: 10px;
+}
+
+.blockBtns {
+ display: flex;
+ gap: 30px;
+}
diff --git a/src/components/AssetsLibrary/ModalAttachmentOptions/index.js b/src/components/AssetsLibrary/ModalAttachmentOptions/index.js
new file mode 100644
index 00000000..e879746a
--- /dev/null
+++ b/src/components/AssetsLibrary/ModalAttachmentOptions/index.js
@@ -0,0 +1,284 @@
+/* Component to render attachment options modal */
+
+import React, { useEffect, useState, useRef, useCallback } from 'react'
+import _ from 'lodash'
+import PropTypes from 'prop-types'
+import styles from './styles.module.scss'
+import { useForm, Controller } from 'react-hook-form'
+import { yupResolver } from '@hookform/resolvers/yup'
+import { toastr } from 'react-redux-toastr'
+import { connect } from 'react-redux'
+import cn from 'classnames'
+import Modal from '../../Modal'
+import FieldUserAutoComplete from '../../FieldUserAutoComplete'
+import FieldLabelDynamic from '../../FieldLabelDynamic'
+import PrimaryButton from '../../Buttons/PrimaryButton'
+import OutlineButton from '../../Buttons/OutlineButton'
+import FieldInput from '../../FieldInput'
+import { assetsLibraryEditFileSchema } from '../../../util/validation'
+import {
+ addProjectAttachmentApi,
+ updateProjectAttachmentApi
+} from '../../../services/projects'
+import { addAttachment, updateAttachment } from '../../../actions/projects'
+
+const ModalAttachmentOptions = ({
+ classsName,
+ theme,
+ onCancel,
+ attachment,
+ members,
+ addAttachment,
+ updateAttachment,
+ projectId,
+ loggedInUser,
+ newAttachments
+}) => {
+ const [isProcessing, setIsProcessing] = useState(false)
+ const shareType = useRef('')
+
+ const {
+ control,
+ register,
+ handleSubmit,
+ reset,
+ watch,
+ formState: { errors, isValid, isDirty }
+ } = useForm({
+ defaultValues: {
+ allowedUsers: []
+ },
+ resolver: attachment ? yupResolver(assetsLibraryEditFileSchema) : null,
+ mode: 'all'
+ })
+
+ const allowedUsers = watch('allowedUsers')
+
+ useEffect(() => {
+ if (attachment) {
+ reset({
+ title: attachment.title,
+ allowedUsers: attachment.allowedUsers || []
+ })
+ }
+ }, [attachment])
+
+ const onEditSubmit = useCallback(
+ data => {
+ setIsProcessing(true)
+ updateProjectAttachmentApi(projectId, attachment.id, data)
+ .then(result => {
+ toastr.success('Success', 'Updated file successfully.')
+ setIsProcessing(false)
+ updateAttachment(result)
+ onCancel()
+ })
+ .catch(e => {
+ setIsProcessing(false)
+ const errorMessage = _.get(
+ e,
+ 'response.data.message',
+ 'Failed to update file.'
+ )
+ toastr.error('Error', errorMessage)
+ })
+ },
+ [attachment, projectId]
+ )
+
+ const onNewSubmit = useCallback(
+ allowedUsers => {
+ let count = newAttachments.length
+ let errorMessage = ''
+ const checkToFinish = () => {
+ count = count - 1
+ if (count === 0) {
+ setIsProcessing(false)
+ if (errorMessage) {
+ toastr.error('Error', errorMessage)
+ } else {
+ toastr.success('Success', 'Added file to the project successfully.')
+ onCancel()
+ }
+ }
+ }
+ setIsProcessing(true)
+ _.forEach(newAttachments, newAttachment => {
+ addProjectAttachmentApi(projectId, {
+ ...newAttachment,
+ allowedUsers
+ })
+ .then(result => {
+ addAttachment(result)
+ checkToFinish()
+ })
+ .catch(e => {
+ errorMessage = _.get(
+ e,
+ 'response.data.message',
+ 'Failed to add file.'
+ )
+ checkToFinish()
+ })
+ })
+ },
+ [newAttachments, projectId]
+ )
+
+ return (
+
+
+
+ {attachment ? 'EDIT FILE' : 'ATTACHMENT OPTIONS'}
+
+
+
+
+ )
+}
+
+ModalAttachmentOptions.defaultProps = {
+ members: [],
+ newAttachments: [],
+ projectId: ''
+}
+
+ModalAttachmentOptions.propTypes = {
+ classsName: PropTypes.string,
+ theme: PropTypes.shape(),
+ onCancel: PropTypes.func,
+ attachment: PropTypes.shape(),
+ newAttachments: PropTypes.arrayOf(PropTypes.shape()),
+ members: PropTypes.arrayOf(PropTypes.shape()),
+ addAttachment: PropTypes.func.isRequired,
+ updateAttachment: PropTypes.func.isRequired,
+ projectId: PropTypes.string,
+ loggedInUser: PropTypes.object
+}
+
+const mapStateToProps = () => {
+ return {}
+}
+
+const mapDispatchToProps = {
+ addAttachment,
+ updateAttachment
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(ModalAttachmentOptions)
diff --git a/src/components/AssetsLibrary/ModalAttachmentOptions/styles.module.scss b/src/components/AssetsLibrary/ModalAttachmentOptions/styles.module.scss
new file mode 100644
index 00000000..cef8ec1e
--- /dev/null
+++ b/src/components/AssetsLibrary/ModalAttachmentOptions/styles.module.scss
@@ -0,0 +1,97 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: center;
+ box-sizing: border-box;
+ background: $white;
+ opacity: 1;
+ position: relative;
+ border-radius: 6px;
+ margin: 0 auto;
+ padding: 15px 20px;
+ width: 600px;
+ max-width: 95vw;
+}
+
+.button {
+ height: 40px;
+
+ span,
+ button {
+ padding: 0 20px;
+ }
+}
+
+.title {
+ @include roboto-medium();
+
+ color: grey;
+ font-size: 16px;
+}
+
+.blockRow {
+ width: 100%;
+}
+
+.blockAddAttachment {
+ border-top: 1px solid $tc-gray-30;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ margin-top: 20px;
+}
+
+.textWhoYouWant {
+ @include roboto;
+
+ width: 100%;
+ margin-top: 30px;
+ margin-bottom: 10px;
+ font-size: 16px;
+}
+
+.blockForm {
+ display: flex;
+ flex-direction: column;
+ gap: 30px;
+ align-items: center;
+}
+
+.blockFormEdit {
+ margin-top: 30px;
+ margin-bottom: 10px;
+}
+
+.blockSelectMember {
+ width: calc(100% + 40px);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ box-sizing: border-box;
+ padding: 10px;
+ background: $tc-gray-20;
+ border-top: 1px solid $tc-gray-30;
+ margin: 10px -20px -15px -20px;
+ border-radius: 0 0 10px 10px;
+}
+
+.textOrOnly {
+ @include roboto-medium();
+
+ font-size: 11px;
+ color: grey;
+ text-transform: uppercase;
+}
+
+.btnShareWith {
+ margin-top: 10px;
+ margin-bottom: 30px;
+}
+
+.blockBtns {
+ display: flex;
+ gap: 30px;
+}
diff --git a/src/components/AssetsLibrary/ProjectMember/index.js b/src/components/AssetsLibrary/ProjectMember/index.js
new file mode 100644
index 00000000..f129b17f
--- /dev/null
+++ b/src/components/AssetsLibrary/ProjectMember/index.js
@@ -0,0 +1,33 @@
+/* Component to render project member */
+
+import React, { useMemo } from 'react'
+import PropTypes from 'prop-types'
+import styles from './styles.module.scss'
+import cn from 'classnames'
+import { PROFILE_URL } from '../../../config/constants'
+import { getFullNameWithFallback } from '../../../util/tc'
+
+const ProjectMember = ({ classsName, memberInfo }) => {
+ const fullName = useMemo(() => getFullNameWithFallback(memberInfo), [
+ memberInfo
+ ])
+ return (
+
+ {fullName}
+
+ )
+}
+
+ProjectMember.defaultProps = {}
+
+ProjectMember.propTypes = {
+ classsName: PropTypes.string,
+ memberInfo: PropTypes.shape()
+}
+
+export default ProjectMember
diff --git a/src/components/AssetsLibrary/ProjectMember/styles.module.scss b/src/components/AssetsLibrary/ProjectMember/styles.module.scss
new file mode 100644
index 00000000..4ac6ceeb
--- /dev/null
+++ b/src/components/AssetsLibrary/ProjectMember/styles.module.scss
@@ -0,0 +1,5 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+}
\ No newline at end of file
diff --git a/src/components/AssetsLibrary/ProjectMembers/index.js b/src/components/AssetsLibrary/ProjectMembers/index.js
new file mode 100644
index 00000000..ad9eeb6c
--- /dev/null
+++ b/src/components/AssetsLibrary/ProjectMembers/index.js
@@ -0,0 +1,60 @@
+/* Component to render list of project member */
+
+import React, { useMemo, useState } from 'react'
+import PropTypes from 'prop-types'
+import _ from 'lodash'
+import styles from './styles.module.scss'
+import ProjectMember from '../ProjectMember'
+import cn from 'classnames'
+
+const ProjectMembers = ({ classsName, members, allowedUsers, maxShownNum }) => {
+ const [showAll, setShowAll] = useState(false)
+ const allowedUserInfos = useMemo(() => {
+ const results = _.uniqBy(
+ _.compact(allowedUsers.map(userId => _.find(members, { userId }))),
+ 'userId'
+ )
+ let extra = 0
+ const maxUsers = [...results]
+ if (maxUsers.length > maxShownNum) {
+ extra = results.length - maxShownNum
+ maxUsers.length = maxShownNum
+ }
+
+ return {
+ all: results,
+ maxUsers,
+ extra
+ }
+ }, [members, allowedUsers, maxShownNum])
+
+ return (
+
+ {(showAll ? allowedUserInfos.all : allowedUserInfos.maxUsers).map(
+ item => (
+
+ )
+ )}
+ {!showAll && allowedUserInfos.extra !== 0 && (
+
+ )}
+
+ )
+}
+
+ProjectMembers.defaultProps = {
+ maxShownNum: 3,
+ allowedUsers: [],
+ members: []
+}
+
+ProjectMembers.propTypes = {
+ classsName: PropTypes.string,
+ maxShownNum: PropTypes.number,
+ allowedUsers: PropTypes.arrayOf(PropTypes.number),
+ members: PropTypes.arrayOf(PropTypes.shape())
+}
+
+export default ProjectMembers
diff --git a/src/components/AssetsLibrary/ProjectMembers/styles.module.scss b/src/components/AssetsLibrary/ProjectMembers/styles.module.scss
new file mode 100644
index 00000000..16cefcce
--- /dev/null
+++ b/src/components/AssetsLibrary/ProjectMembers/styles.module.scss
@@ -0,0 +1,17 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+}
+
+.btn {
+ border: none;
+ background-color: transparent;
+ margin: 0;
+ outline: none !important;
+ border: 1px solid $tc-gray-30;
+ border-radius: 6px;
+ padding: 0 3px;
+}
\ No newline at end of file
diff --git a/src/components/AssetsLibrary/TabCommon/index.js b/src/components/AssetsLibrary/TabCommon/index.js
new file mode 100644
index 00000000..2b319c04
--- /dev/null
+++ b/src/components/AssetsLibrary/TabCommon/index.js
@@ -0,0 +1,49 @@
+/* Component to render tab ui */
+
+import React from 'react'
+import PropTypes from 'prop-types'
+import styles from './styles.module.scss'
+import cn from 'classnames'
+
+const TabCommon = ({ items, classsName, selectedIndex, onSelect }) => {
+ return (
+
+ {items.map((item, index) => (
+
+ ))}
+
+ )
+}
+
+TabCommon.defaultProps = {
+ items: [],
+ classsName: '',
+ onSelect: () => {}
+}
+
+TabCommon.propTypes = {
+ items: PropTypes.arrayOf(
+ PropTypes.shape({
+ label: PropTypes.string,
+ count: PropTypes.number
+ })
+ ),
+ classsName: PropTypes.string,
+ selectedIndex: PropTypes.number,
+ onSelect: PropTypes.func
+}
+
+export default TabCommon
diff --git a/src/components/AssetsLibrary/TabCommon/styles.module.scss b/src/components/AssetsLibrary/TabCommon/styles.module.scss
new file mode 100644
index 00000000..191cee55
--- /dev/null
+++ b/src/components/AssetsLibrary/TabCommon/styles.module.scss
@@ -0,0 +1,50 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ gap: 20px;
+}
+
+.blockTab {
+ padding: 0 15px;
+ margin: 0;
+ border: none;
+ background: transparent;
+ display: flex;
+ border-radius: 20px;
+ height: 30px;
+ align-items: flex-end;
+ gap: 5px;
+ outline: none !important;
+
+ &.selected {
+ background-color: $tc-gray-30;
+
+ * {
+ font-weight: 700;
+ }
+ .textCount {
+ background-color: $tc-gray-80;
+ }
+ }
+}
+
+.textLabel {
+ @include roboto;
+
+ font-size: 16px;
+ line-height: 30px;
+ color: $tc-gray-90;
+}
+
+.textCount {
+ @include roboto;
+
+ background-color: grey;
+ border-radius: 9px;
+ color: #fff;
+ font-size: 10px;
+ line-height: 12px;
+ padding: 0 4px;
+ margin-bottom: 7px;
+}
diff --git a/src/components/AssetsLibrary/TableAssets/index.js b/src/components/AssetsLibrary/TableAssets/index.js
new file mode 100644
index 00000000..e5dc40c4
--- /dev/null
+++ b/src/components/AssetsLibrary/TableAssets/index.js
@@ -0,0 +1,158 @@
+/* Component to render assets table */
+
+import React, { useMemo } from 'react'
+import PropTypes from 'prop-types'
+import _ from 'lodash'
+import moment from 'moment'
+import styles from './styles.module.scss'
+import Table from '../../Table'
+import IconThreeDot from '../../Icons/IconThreeDot'
+import DropdownMenu from '../../DropdownMenu'
+import DownloadFile from '../DownloadFile'
+import IconFile from '../../Icons/IconFile'
+import cn from 'classnames'
+import {
+ PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS,
+ PROJECT_ASSETS_SHARED_WITH_ADMIN
+} from '../../../config/constants'
+import ProjectMembers from '../ProjectMembers'
+import ProjectMember from '../ProjectMember'
+
+const TableAssets = ({
+ classsName,
+ title,
+ onEdit,
+ onRemove,
+ datas,
+ isLink,
+ projectId,
+ members,
+ loggedInUser,
+ isAdmin
+}) => {
+ const displayAssets = useMemo(
+ () =>
+ datas.map(item => {
+ const titles = item.title.split('.')
+ const owner = _.find(members, { userId: item.createdBy })
+ const canEdit =
+ `${item.createdBy}` === `${loggedInUser.userId}` || isAdmin
+ return {
+ ...item,
+ fileType: titles[titles.length - 1],
+ owner,
+ updatedAtString: item.updatedAt
+ ? moment(item.updatedAt).format('MM/DD/YYYY h:mm A')
+ : '—',
+ canEdit
+ }
+ }),
+ [datas, members, loggedInUser, isAdmin]
+ )
+ return (
+
+
{title}
+
+
(
+
+
+
+
+
+ {isLink ? (
+
+ {item.title}
+
+ ) : (
+
+ )}
+
+
+ {!item.allowedUsers && PROJECT_ASSETS_SHARED_WITH_ALL_MEMBERS}
+ {item.allowedUsers &&
+ item.allowedUsers === 0 &&
+ PROJECT_ASSETS_SHARED_WITH_ADMIN}
+ {item.allowedUsers && item.allowedUsers !== 0 && (
+
+ )}
+
+
+ {!item.owner && !item.createdBy && '—'}
+ {!item.owner && item.createdBy !== 'CoderBot' && 'Unknown'}
+ {!item.owner && item.createdBy === 'CoderBot' && 'CoderBot'}
+ {!!item.owner && }
+
+
+ {item.updatedAtString}
+
+
+ {item.canEdit && (
+ {
+ if (menu === 'Edit') {
+ onEdit(item)
+ } else if (menu === 'Remove') {
+ onRemove(item)
+ }
+ }}
+ options={['Edit', 'Remove']}
+ >
+
+
+ )}
+
+
+ ))}
+ />
+
+ )
+}
+
+TableAssets.defaultProps = {
+ title: '',
+ onEdit: () => {},
+ onRemove: () => {},
+ datas: [],
+ isLink: false,
+ isAdmin: false,
+ members: []
+}
+
+TableAssets.propTypes = {
+ classsName: PropTypes.string,
+ projectId: PropTypes.string,
+ title: PropTypes.string,
+ isAdmin: PropTypes.bool,
+ onEdit: PropTypes.func,
+ onRemove: PropTypes.func,
+ datas: PropTypes.arrayOf(PropTypes.shape()),
+ members: PropTypes.arrayOf(PropTypes.shape()),
+ isLink: PropTypes.bool,
+ loggedInUser: PropTypes.object
+}
+
+export default TableAssets
diff --git a/src/components/AssetsLibrary/TableAssets/styles.module.scss b/src/components/AssetsLibrary/TableAssets/styles.module.scss
new file mode 100644
index 00000000..f719e5c7
--- /dev/null
+++ b/src/components/AssetsLibrary/TableAssets/styles.module.scss
@@ -0,0 +1,34 @@
+@import '../../../styles/includes';
+
+.container {
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+.textTitle {
+ color: $tc-gray-90;
+ @include roboto-bold();
+
+ font-size: 16px;
+ line-height: 20px;
+}
+
+.colMenu {
+ width: 50px;
+}
+
+.blockItem {
+ @include roboto;
+
+ font-size: 14px;
+ color: $tc-gray-90;
+
+ a {
+ color: $tc-gray-90;
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+}
diff --git a/src/components/AssetsLibrary/index.js b/src/components/AssetsLibrary/index.js
new file mode 100644
index 00000000..e69de29b
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 (
+
+
+
+
+ {
+ !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 8b658549..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'
@@ -20,7 +20,6 @@ import { getResourceRoleByName } from '../../../util/tc'
import { loadGroupDetails } from '../../../actions/challenges'
import {
REVIEW_TYPES,
- CONNECT_APP_URL,
PHASE_PRODUCT_CHALLENGE_ID_FIELD,
MULTI_ROUND_CHALLENGE_TEMPLATE_ID,
DS_TRACK_ID
@@ -113,17 +112,15 @@ const ChallengeView = ({
- Project: {projectDetail ? projectDetail.name : ''}
+ Project:
+ {projectDetail ? projectDetail.name : ''}
+
+
{selectedMilestone &&
}
diff --git a/src/components/ChallengeEditor/ChallengeViewTabs/index.js b/src/components/ChallengeEditor/ChallengeViewTabs/index.js
index 84ee5676..9c7d301f 100644
--- a/src/components/ChallengeEditor/ChallengeViewTabs/index.js
+++ b/src/components/ChallengeEditor/ChallengeViewTabs/index.js
@@ -55,7 +55,11 @@ const ChallengeViewTabs = ({
loggedInUser,
onApproveChallenge,
createResource,
- deleteResource
+ deleteResource,
+ loadSubmissions,
+ totalSubmissions,
+ submissionsPerPage,
+ page
}) => {
const [selectedTab, setSelectedTab] = useState(0)
const [showAddResourceModal, setShowAddResourceModal] = useState(false)
@@ -114,7 +118,7 @@ const ChallengeViewTabs = ({
})
return s
})
- }, [challengeSubmissions, allResources])
+ }, [challengeSubmissions, allResources, page])
const isTask = _.get(challenge, 'task.isTask', false)
@@ -298,7 +302,7 @@ const ChallengeViewTabs = ({
>
RESOURCES
- {challengeSubmissions.length ? (
+ {totalSubmissions ? (
- SUBMISSIONS ({submissions.length})
+ SUBMISSIONS ({totalSubmissions})
) : null}
@@ -353,6 +357,10 @@ const ChallengeViewTabs = ({
submissions={submissions}
token={token}
loggedInUserResource={loggedInUserResource}
+ loadSubmissions={loadSubmissions}
+ totalSubmissions={totalSubmissions}
+ submissionsPerPage={submissionsPerPage}
+ page={page}
/>
)}
{showAddResourceModal ? (
{
const options = milestones.map(type => ({ label: type.name, value: type.id }))
@@ -28,10 +26,6 @@ const MilestoneField = ({ milestones, onUpdateSelect, disabled, projectId, selec
isDisabled={disabled}
/>
-
-
-
>
)
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 59149d21..966452fc 100644
--- a/src/components/ChallengeEditor/Submissions/Submissions.module.scss
+++ b/src/components/ChallengeEditor/Submissions/Submissions.module.scss
@@ -3,7 +3,6 @@
$tc-black : #151516;
$tc-gray-10: #d5d5d5;
$tc-gray-50: #808080;
-$tc-gray-90: #2a2a2b;
$tc-light-blue: #15acec;
$tc-dark-blue: #0681ff;
$tc-dark-blue-110: #006ad7;
@@ -240,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;
}
}
@@ -389,3 +401,25 @@ $base-unit: 5px;
overflow: hidden;
}
}
+
+.paginationWrapper {
+ display: flex;
+ margin-top: 30px;
+ flex: 1;
+ justify-content: center;
+}
+
+.footer {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 30px;
+}
+
+.perPageContainer {
+ margin-right: 20px;
+ max-width: 150px;
+}
+
+.paginationContainer {
+ display: flex;
+}
diff --git a/src/components/ChallengeEditor/Submissions/index.js b/src/components/ChallengeEditor/Submissions/index.js
index 13428040..e3b71051 100644
--- a/src/components/ChallengeEditor/Submissions/index.js
+++ b/src/components/ChallengeEditor/Submissions/index.js
@@ -7,16 +7,13 @@ import React from 'react'
import PT from 'prop-types'
import moment from 'moment'
import _ from 'lodash'
-import { STUDIO_URL, SUBMISSION_REVIEW_APP_URL, getTCMemberURL } from '../../../config/constants'
+import { PAGINATION_PER_PAGE_OPTIONS, STUDIO_URL, SUBMISSION_REVIEW_APP_URL, getTCMemberURL } from '../../../config/constants'
import { PrimaryButton } from '../../Buttons'
import AlertModal from '../../Modal/AlertModal'
import cn from 'classnames'
import ReactSVG from 'react-svg'
import {
getRatingLevel,
- sortList,
- getProvisionalScore,
- getFinalScore,
checkDownloadSubmissionRoles,
checkAdmin
} from '../../../util/tc'
@@ -29,10 +26,16 @@ 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'
+import Select from '../../Select'
+import Pagination from 'react-js-pagination'
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
@@ -48,157 +51,21 @@ class SubmissionsComponent extends React.Component {
},
isShowInformation: false,
memberOfModal: '',
- sortedSubmissions: [],
+ submissions: [],
downloadingAll: false,
- alertMessage: ''
+ alertMessage: '',
+ selectedSubmissionId: '',
+ showArtifactsListModal: false,
+ showRatingsListModal: false
}
- this.getSubmissionsSortParam = this.getSubmissionsSortParam.bind(this)
- this.updateSortedSubmissions = this.updateSortedSubmissions.bind(this)
- this.sortSubmissions = this.sortSubmissions.bind(this)
- this.onSortChange = this.onSortChange.bind(this)
this.checkIsReviewPhaseComplete = this.checkIsReviewPhaseComplete.bind(
this
)
+ this.downloadSubmission = this.downloadSubmission.bind(this)
+ this.handlePageChange = this.handlePageChange.bind(this)
+ this.handlePerPageChange = this.handlePerPageChange.bind(this)
}
- componentDidMount () {
- this.updateSortedSubmissions()
- }
-
- /**
- * Get submission sort parameter
- */
- getSubmissionsSortParam () {
- const { submissionsSort } = this.state
- let { field, sort } = submissionsSort
- if (!field) {
- field = 'Submission Date' // default field for submission sorting
- }
-
- if (!sort) {
- sort = 'asc' // default order for submission sorting
- }
-
- return {
- field,
- sort
- }
- }
-
- /**
- * Update sorted submission array
- */
- updateSortedSubmissions () {
- const { submissions } = this.props
- const sortedSubmissions = _.cloneDeep(submissions)
- this.sortSubmissions(sortedSubmissions)
- this.setState({ sortedSubmissions })
- }
-
- /**
- * Sort array of submission
- * @param {Array} submissions array of submission
- */
- sortSubmissions (submissions) {
- const { field, sort } = this.getSubmissionsSortParam()
- let isHaveFinalScore = false
- if (field === 'Initial / Final Score') {
- isHaveFinalScore = _.some(
- submissions,
- s => !_.isNil(s.reviewSummation && s.reviewSummation[0].aggregateScore)
- )
- }
- return sortList(submissions, field, sort, (a, b) => {
- let valueA = 0
- let valueB = 0
- let valueIsString = false
- switch (field) {
- case 'Country': {
- valueA = a.registrant ? a.registrant.countryCode : ''
- valueB = b.registrant ? b.registrant.countryCode : ''
- valueIsString = true
- break
- }
- case 'Rating': {
- valueA = a.registrant ? a.registrant.rating : 0
- valueB = b.registrant ? b.registrant.rating : 0
- break
- }
- case 'Username': {
- valueA = _.get(a.registrant, 'memberHandle', '').toLowerCase()
- valueB = _.get(b.registrant, 'memberHandle', '').toLowerCase()
- valueIsString = true
- break
- }
- case 'Email': {
- valueA = _.get(a.registrant, 'email', '').toLowerCase()
- valueB = _.get(b.registrant, 'email', '').toLowerCase()
- valueIsString = true
- break
- }
- case 'Time':
- valueA = new Date(a.submissions && a.submissions[0].submissionTime)
- valueB = new Date(b.submissions && b.submissions[0].submissionTime)
- break
- case 'Submission Date': {
- valueA = new Date(a.created)
- valueB = new Date(b.created)
- break
- }
- case 'Initial / Final Score': {
- if (isHaveFinalScore) {
- valueA = getFinalScore(a)
- valueB = getFinalScore(b)
- } else {
- valueA = !_.isEmpty(a.review) && a.review[0].score
- valueB = !_.isEmpty(b.review) && b.review[0].score
- }
- break
- }
- case 'Final Rank': {
- if (this.checkIsReviewPhaseComplete()) {
- valueA = a.finalRank ? a.finalRank : 0
- valueB = b.finalRank ? b.finalRank : 0
- }
- break
- }
- case 'Provisional Rank': {
- valueA = a.provisionalRank ? a.provisionalRank : 0
- valueB = b.provisionalRank ? b.provisionalRank : 0
- break
- }
- case 'Final Score': {
- valueA = getFinalScore(a)
- valueB = getFinalScore(b)
- break
- }
- case 'Provisional Score': {
- valueA = getProvisionalScore(a)
- valueB = getProvisionalScore(b)
- break
- }
- default:
- }
-
- if (valueIsString === false) {
- if (valueA === '-') valueA = 0
- if (valueB === '-') valueB = 0
- }
-
- return {
- valueA,
- valueB,
- valueIsString
- }
- })
- }
-
- onSortChange (sort) {
- this.setState({
- submissionsSort: sort
- })
- this.updateSortedSubmissions()
- }
/**
* Check if review phase complete
*/
@@ -220,17 +87,96 @@ 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.'
+ })
+ }
+ })
+ })
+ }
+
+ /**
+ * Update filter for getting project by pagination
+ * @param {Number} perPageNumber per page number
+ */
+ handlePerPageChange (option) {
+ const perPageNumber = option.value
+ const {
+ submissionsPerPage,
+ loadSubmissions,
+ challenge: {
+ id
+ }
+ } = this.props
+
+ if (submissionsPerPage !== perPageNumber) {
+ loadSubmissions(id, {
+ page: 1,
+ perPage: perPageNumber
+ })
+ }
+ }
+
+ /**
+ * Update filter for getting project by pagination
+ * @param {Number} pageNumber page number
+ */
+ async handlePageChange (pageNumber) {
+ const {
+ page,
+ submissionsPerPage,
+ loadSubmissions,
+ challenge: {
+ id
+ }
+ } = this.props
+
+ if (page !== pageNumber) {
+ loadSubmissions(id, {
+ page: pageNumber,
+ perPage: submissionsPerPage
+ })
+ }
+ }
+
render () {
- const { challenge, token, loggedInUserResource } = this.props
+ const { challenge, token, loggedInUserResource, page, submissionsPerPage, totalSubmissions, submissions } = this.props
const { checkpoints, track, type, tags } = challenge
const canDownloadSubmission =
(loggedInUserResource && checkDownloadSubmissionRoles(loggedInUserResource.roles)) ||
checkAdmin(token)
- const { field, sort } = this.getSubmissionsSortParam()
- const revertSort = sort === 'desc' ? 'asc' : 'desc'
-
- const { sortedSubmissions, downloadingAll, alertMessage } = this.state
+ const { downloadingAll, alertMessage } = this.state
const renderSubmission = s => (
@@ -272,7 +218,7 @@ class SubmissionsComponent extends React.Component {
const isBugHunt = _.includes(tags, 'Bug Hunt')
// copy colorStyle from registrants to submissions
- _.forEach(sortedSubmissions, s => {
+ _.forEach(submissions, s => {
if (s.registrant && s.registrant.colorStyle && !s.colorStyle) {
const { colorStyle } = s.registrant
/* eslint-disable no-param-reassign */
@@ -297,7 +243,7 @@ class SubmissionsComponent extends React.Component {
ROUND 2 (FINAL) SUBMISSIONS
- {sortedSubmissions.map(renderSubmission)}
+ {submissions.map(renderSubmission)}
{checkpoints.length > 0 && (
@@ -341,117 +287,20 @@ class SubmissionsComponent extends React.Component {
{!isF2F && !isBugHunt && (
-
+ Rating
|
)}
-
+ Username
|
-
+ Email
|
-
+ Submission Date
|
-
+ Initial / Final Score
|
|
- {sortedSubmissions.map(s => {
+ {submissions.map(s => {
const rating = s.registrant && !_.isNil(s.registrant.rating)
? s.registrant.rating
: '-'
@@ -544,40 +393,38 @@ class SubmissionsComponent extends React.Component {
{canDownloadSubmission ? (
-
+
+
+
+
+
+
+
+
+
+
+
+
+
| ) : null}
)
@@ -586,6 +433,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 ? (
@@ -604,7 +481,7 @@ class SubmissionsComponent extends React.Component {
const allFiles = []
let downloadedFile = 0
const checkToCompressFiles = () => {
- if (downloadedFile === sortedSubmissions.length) {
+ if (downloadedFile === submissions.length) {
if (downloadedFile > 0) {
compressFiles(allFiles, 'all-submissions.zip', () => {
this.setState({
@@ -619,7 +496,7 @@ class SubmissionsComponent extends React.Component {
}
}
checkToCompressFiles()
- _.forEach(sortedSubmissions, (submission) => {
+ _.forEach(submissions, (submission) => {
let fileName = submission.legacySubmissionId
if (!fileName) {
fileName = submission.id
@@ -646,6 +523,30 @@ class SubmissionsComponent extends React.Component {
) : null}
+
+
{alertMessage ? (
member.userId === loginUserId)
if (warnMessage) {
return
@@ -496,6 +499,8 @@ class ChallengeList extends Component {
currentBillingAccount={currentBillingAccount}
updateProject={updateProject}
projectId={activeProject.id}
+ isMemberOfActiveProject={isMemberOfActiveProject}
+ isManager={isManager}
/>
) : (
@@ -507,6 +512,9 @@ class ChallengeList extends Component {