diff --git a/config/default.js b/config/default.js index 27ee39b..b82efe6 100644 --- a/config/default.js +++ b/config/default.js @@ -3,10 +3,10 @@ module.exports = { DEBOUNCE_ON_CHANGE_TIME: 150, }, API: { - V5: 'https://api.topcoder-dev.com/v5', - V3: 'https://api.topcoder-dev.com/v3', + V5: "https://api.topcoder-dev.com/v5", + V3: "https://api.topcoder-dev.com/v3", }, URL: { - COMMUNITY_APP: 'https://community-app.topcoder-dev.com', + COMMUNITY_APP: "https://community-app.topcoder-dev.com", } } diff --git a/config/jest/setup.js b/config/jest/setup.js new file mode 100644 index 0000000..103b78d --- /dev/null +++ b/config/jest/setup.js @@ -0,0 +1,3 @@ +const config = require("config"); + +global.process.env = {...global.process.env, ...config}; diff --git a/jest.config.js b/jest.config.js index 0c4b51c..116f278 100644 --- a/jest.config.js +++ b/jest.config.js @@ -4,10 +4,12 @@ module.exports = { "^.+\\.(j|t)sx?$": "babel-jest", }, moduleNameMapper: { - "\\.(css)$": "identity-obj-proxy", - "\\.svg$": "/__mocks__/fileMock.js", + "\\.(css|scss)$": "identity-obj-proxy", + "\\.(png|eot|otf|ttf|woff|woff2|svg)$": "/__mocks__/fileMock.js", + }, setupFilesAfterEnv: [ "../node_modules/@testing-library/jest-dom/dist/index.js", + "../config/jest/setup.js", ], }; diff --git a/package-lock.json b/package-lock.json index 69e9df7..930ed65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,31 +13,30 @@ } }, "@babel/compat-data": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.12.tgz", - "integrity": "sha512-3eJJ841uKxeV8dcN/2yGEUy+RfgQspPEgQat85umsE1rotuquQ2AbIub4S6j7c50a2d+4myc+zSlnXeIHrOnhQ==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz", + "integrity": "sha512-ltnibHKR1VnrU4ymHyQ/CXtNXI6yZC0oJThyW78Hft8XndANwi+9H+UIklBDraIjFEJzw8wmcM427oDd9KS5wA==", "dev": true }, "@babel/core": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.13.tgz", - "integrity": "sha512-1xEs9jZAyKIouOoCmpsgk/I26PoKyvzQ2ixdRpRzfbcp1fL+ozw7TUgdDgwonbTovqRaTfRh50IXuw4QrWO0GA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz", + "integrity": "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.13.9", "@babel/helper-compilation-targets": "^7.13.13", - "@babel/helper-module-transforms": "^7.13.12", + "@babel/helper-module-transforms": "^7.13.14", "@babel/helpers": "^7.13.10", - "@babel/parser": "^7.13.13", + "@babel/parser": "^7.13.15", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/traverse": "^7.13.15", + "@babel/types": "^7.13.14", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.1.2", - "lodash": "^4.17.19", "semver": "^6.3.0", "source-map": "^0.5.0" }, @@ -154,9 +153,9 @@ } }, "@babel/helper-define-polyfill-provider": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz", - "integrity": "sha512-nXuzCSwlJ/WKr8qxzW816gwyT6VZgiJG17zR40fou70yfAcqjoNyTLl/DQ+FExw5Hx5KNqshmN8Ldl/r2N7cTg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.2.0.tgz", + "integrity": "sha512-JT8tHuFjKBo8NnaUbblz7mIu1nnvUDiHVjXXkulZULyidvo/7P6TY7+YqpV37IfF+KUFxmlK04elKtGKXaiVgw==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.13.0", @@ -244,9 +243,9 @@ } }, "@babel/helper-module-transforms": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.12.tgz", - "integrity": "sha512-7zVQqMO3V+K4JOOj40kxiCrMf6xlQAkewBB0eu2b03OO/Q21ZutOzjpfD79A5gtE/2OWi1nv625MrDlGlkbknQ==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.13.14.tgz", + "integrity": "sha512-QuU/OJ0iAOSIatyVZmfqB0lbkVP0kDRiKj34xy+QNsnVZi/PA6BoSoreeqnxxa9EHFAIL0R9XOaAR/G9WlIy5g==", "dev": true, "requires": { "@babel/helper-module-imports": "^7.13.12", @@ -255,8 +254,8 @@ "@babel/helper-split-export-declaration": "^7.12.13", "@babel/helper-validator-identifier": "^7.12.11", "@babel/template": "^7.12.13", - "@babel/traverse": "^7.13.0", - "@babel/types": "^7.13.12" + "@babel/traverse": "^7.13.13", + "@babel/types": "^7.13.14" } }, "@babel/helper-optimise-call-expression": { @@ -371,9 +370,9 @@ } }, "@babel/parser": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.13.tgz", - "integrity": "sha512-OhsyMrqygfk5v8HmWwOzlYjJrtLaFhF34MrfG/Z73DgYCI6ojNUTUp2TYbtnjo8PegeJp12eamsNettCQjKjVw==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.13.15.tgz", + "integrity": "sha512-b9COtcAlVEQljy/9fbcMHpG+UIW9ReF+gpaxDHTlZd0c6/UU9ng8zdySAW9sRTzpvcdCHn6bUcbuYUgGzLAWVQ==", "dev": true }, "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { @@ -388,9 +387,9 @@ } }, "@babel/plugin-proposal-async-generator-functions": { - "version": "7.13.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz", - "integrity": "sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.15.tgz", + "integrity": "sha512-VapibkWzFeoa6ubXy/NgV5U2U4MVnUlvnx6wo1XhlsaTrLYWE0UFpDQsVrmn22q5CzeloqJ8gEMHSKxuee6ZdA==", "dev": true, "requires": { "@babel/helper-plugin-utils": "^7.13.0", @@ -928,9 +927,9 @@ } }, "@babel/plugin-transform-regenerator": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz", - "integrity": "sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz", + "integrity": "sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==", "dev": true, "requires": { "regenerator-transform": "^0.14.2" @@ -946,16 +945,16 @@ } }, "@babel/plugin-transform-runtime": { - "version": "7.13.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.10.tgz", - "integrity": "sha512-Y5k8ipgfvz5d/76tx7JYbKQTcgFSU6VgJ3kKQv4zGTKr+a9T/KBvfRvGtSFgKDQGt/DBykQixV0vNWKIdzWErA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.13.15.tgz", + "integrity": "sha512-d+ezl76gx6Jal08XngJUkXM4lFXK/5Ikl9Mh4HKDxSfGJXmZ9xG64XT2oivBzfxb/eQ62VfvoMkaCZUKJMVrBA==", "dev": true, "requires": { - "@babel/helper-module-imports": "^7.12.13", + "@babel/helper-module-imports": "^7.13.12", "@babel/helper-plugin-utils": "^7.13.0", - "babel-plugin-polyfill-corejs2": "^0.1.4", - "babel-plugin-polyfill-corejs3": "^0.1.3", - "babel-plugin-polyfill-regenerator": "^0.1.2", + "babel-plugin-polyfill-corejs2": "^0.2.0", + "babel-plugin-polyfill-corejs3": "^0.2.0", + "babel-plugin-polyfill-regenerator": "^0.2.0", "semver": "^6.3.0" } }, @@ -1025,17 +1024,17 @@ } }, "@babel/preset-env": { - "version": "7.13.12", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.12.tgz", - "integrity": "sha512-JzElc6jk3Ko6zuZgBtjOd01pf9yYDEIH8BcqVuYIuOkzOwDesoa/Nz4gIo4lBG6K861KTV9TvIgmFuT6ytOaAA==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz", + "integrity": "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.12", - "@babel/helper-compilation-targets": "^7.13.10", + "@babel/compat-data": "^7.13.15", + "@babel/helper-compilation-targets": "^7.13.13", "@babel/helper-plugin-utils": "^7.13.0", "@babel/helper-validator-option": "^7.12.17", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.13.12", - "@babel/plugin-proposal-async-generator-functions": "^7.13.8", + "@babel/plugin-proposal-async-generator-functions": "^7.13.15", "@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-dynamic-import": "^7.13.8", "@babel/plugin-proposal-export-namespace-from": "^7.12.13", @@ -1083,7 +1082,7 @@ "@babel/plugin-transform-object-super": "^7.12.13", "@babel/plugin-transform-parameters": "^7.13.0", "@babel/plugin-transform-property-literals": "^7.12.13", - "@babel/plugin-transform-regenerator": "^7.12.13", + "@babel/plugin-transform-regenerator": "^7.13.15", "@babel/plugin-transform-reserved-words": "^7.12.13", "@babel/plugin-transform-shorthand-properties": "^7.12.13", "@babel/plugin-transform-spread": "^7.13.0", @@ -1093,10 +1092,10 @@ "@babel/plugin-transform-unicode-escapes": "^7.12.13", "@babel/plugin-transform-unicode-regex": "^7.12.13", "@babel/preset-modules": "^0.1.4", - "@babel/types": "^7.13.12", - "babel-plugin-polyfill-corejs2": "^0.1.4", - "babel-plugin-polyfill-corejs3": "^0.1.3", - "babel-plugin-polyfill-regenerator": "^0.1.2", + "@babel/types": "^7.13.14", + "babel-plugin-polyfill-corejs2": "^0.2.0", + "babel-plugin-polyfill-corejs3": "^0.2.0", + "babel-plugin-polyfill-regenerator": "^0.2.0", "core-js-compat": "^3.9.0", "semver": "^6.3.0" } @@ -1158,17 +1157,17 @@ } }, "@babel/traverse": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.13.tgz", - "integrity": "sha512-CblEcwmXKR6eP43oQGG++0QMTtCjAsa3frUuzHoiIJWpaIIi8dwMyEFUJoXRLxagGqCK+jALRwIO+o3R9p/uUg==", + "version": "7.13.15", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.13.15.tgz", + "integrity": "sha512-/mpZMNvj6bce59Qzl09fHEs8Bt8NnpEDQYleHUPZQ3wXUMvXi+HJPLars68oAbmp839fGoOkv2pSL2z9ajCIaQ==", "dev": true, "requires": { "@babel/code-frame": "^7.12.13", "@babel/generator": "^7.13.9", "@babel/helper-function-name": "^7.12.13", "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.13.13", - "@babel/types": "^7.13.13", + "@babel/parser": "^7.13.15", + "@babel/types": "^7.13.14", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1191,9 +1190,9 @@ } }, "@babel/types": { - "version": "7.13.13", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.13.tgz", - "integrity": "sha512-kt+EpC6qDfIaqlP+DIbIJOclYy/A1YXs9dAf/ljbi+39Bcbc073H6jKVpXEr/EoIh5anGn5xq/yRVzKl+uIc9w==", + "version": "7.13.14", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.14.tgz", + "integrity": "sha512-A2aa3QTkWoyqsZZFl56MLUsfmh7O0gN41IPvXAE/++8ojpbz12SszD7JEGYVdn4f9Kt4amIei07swF1h4AqmmQ==", "dev": true, "requires": { "@babel/helper-validator-identifier": "^7.12.11", @@ -1217,6 +1216,19 @@ "minimist": "^1.2.0" } }, + "@hapi/hoek": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.1.1.tgz", + "integrity": "sha512-CAEbWH7OIur6jEOzaai83jq3FmKmv4PmX1JYfs9IrYcGEVI/lyL1EXJGCj7eFVJ0bg5QR8LMxBlEtA+xKiLpFw==" + }, + "@hapi/topo": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", + "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1384,9 +1396,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -1562,13 +1574,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -1786,13 +1798,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -1957,9 +1969,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -2135,13 +2147,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -2306,9 +2318,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -2447,13 +2459,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -2623,10 +2635,28 @@ "integrity": "sha512-DetpxZw1fzPD5xUBrIAoplLChO2VB8DlL5Gg+I1IR9b2wPqYIca2WSUxL5g1vLeR4MsQq1NeWriXAVffV+U1Fw==", "dev": true }, + "@sideway/address": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.1.tgz", + "integrity": "sha512-+I5aaQr3m0OAmMr7RQ3fR9zx55sejEYR2BFJaxL+zT3VM2611X0SHvPWIbAUBZVTn/YzYKbV8gJ2oT/QELknfQ==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.0.tgz", + "integrity": "sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "@sinonjs/commons": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", - "integrity": "sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw==", + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", "dev": true, "requires": { "type-detect": "4.0.8" @@ -3697,9 +3727,9 @@ "dev": true }, "axe-core": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.3.tgz", - "integrity": "sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.4.tgz", + "integrity": "sha512-Pdgfv6iP0gNx9ejRGa3zE7Xgkj/iclXqLfe7BnatdZz0QnLZ3jrRHUVH8wNSdN68w05Sk3ShGTb3ydktMTooig==", "dev": true }, "axobject-query": { @@ -3825,33 +3855,33 @@ } }, "babel-plugin-polyfill-corejs2": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.1.10.tgz", - "integrity": "sha512-DO95wD4g0A8KRaHKi0D51NdGXzvpqVLnLu5BTvDlpqUEpTmeEtypgC1xqesORaWmiUOQI14UHKlzNd9iZ2G3ZA==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.2.0.tgz", + "integrity": "sha512-9bNwiR0dS881c5SHnzCmmGlMkJLl0OUZvxrxHo9w/iNoRuqaPjqlvBf4HrovXtQs/au5yKkpcdgfT1cC5PAZwg==", "dev": true, "requires": { - "@babel/compat-data": "^7.13.0", - "@babel/helper-define-polyfill-provider": "^0.1.5", + "@babel/compat-data": "^7.13.11", + "@babel/helper-define-polyfill-provider": "^0.2.0", "semver": "^6.1.1" } }, "babel-plugin-polyfill-corejs3": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.1.7.tgz", - "integrity": "sha512-u+gbS9bbPhZWEeyy1oR/YaaSpod/KDT07arZHb80aTpl8H5ZBq+uN1nN9/xtX7jQyfLdPfoqI4Rue/MQSWJquw==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.2.0.tgz", + "integrity": "sha512-zZyi7p3BCUyzNxLx8KV61zTINkkV65zVkDAFNZmrTCRVhjo1jAS+YLvDJ9Jgd/w2tsAviCwFHReYfxO3Iql8Yg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.1.5", - "core-js-compat": "^3.8.1" + "@babel/helper-define-polyfill-provider": "^0.2.0", + "core-js-compat": "^3.9.1" } }, "babel-plugin-polyfill-regenerator": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.1.6.tgz", - "integrity": "sha512-OUrYG9iKPKz8NxswXbRAdSwF0GhRdIEMTloQATJi4bDuFqrXaXcCUT/VGNrr8pBcjMh1RxZ7Xt9cytVJTJfvMg==", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.2.0.tgz", + "integrity": "sha512-J7vKbCuD2Xi/eEHxquHN14bXAW9CXtecwuLrOIDJtcZzTaPzV1VdEfoUf9AzcRBMolKUQKM9/GVojeh0hFiqMg==", "dev": true, "requires": { - "@babel/helper-define-polyfill-provider": "^0.1.5" + "@babel/helper-define-polyfill-provider": "^0.2.0" } }, "babel-plugin-react-css-modules": { @@ -3944,9 +3974,9 @@ } }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "base": { @@ -4461,9 +4491,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001204", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz", - "integrity": "sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==" + "version": "1.0.30001208", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001208.tgz", + "integrity": "sha512-OE5UE4+nBOro8Dyvv0lfx+SRtfVIOM9uhKqFmJeUbGriqhhStgp1A0OyBpgy3OUF8AhYCT+PVwPC1gMl2ZcQMA==" }, "capture-exit": { "version": "2.0.0", @@ -4520,9 +4550,9 @@ }, "dependencies": { "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "optional": true, "requires": { @@ -4590,21 +4620,10 @@ "dev": true }, "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true }, "ci-info": { "version": "2.0.0", @@ -4646,9 +4665,9 @@ } }, "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", + "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" }, "clean-css": { "version": "4.2.3", @@ -4984,9 +5003,9 @@ "dev": true }, "core-js-compat": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.9.1.tgz", - "integrity": "sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.10.1.tgz", + "integrity": "sha512-ZHQTdTPkqvw2CeHiZC970NNJcnwzT6YIueDMASKt+p3WbZsLXOcoD392SkcWhkC0wBBHhlfhqGKKsNCQUozYtg==", "dev": true, "requires": { "browserslist": "^4.16.3", @@ -5015,9 +5034,9 @@ } }, "core-js-pure": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.9.1.tgz", - "integrity": "sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==", + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.10.1.tgz", + "integrity": "sha512-PeyJH2SE0KuxY5eCGNWA+W+CeDpB6M1PN3S7Am7jSv/Ttuxz2SnWbIiVQOn/TDaGaGtxo8CRWHkXwJscbUHtVw==", "dev": true }, "core-util-is": { @@ -5437,9 +5456,9 @@ } }, "css-tree": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.2.tgz", - "integrity": "sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz", + "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==", "requires": { "mdn-data": "2.0.14", "source-map": "^0.6.1" @@ -5552,9 +5571,9 @@ } }, "date-fns": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz", - "integrity": "sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.20.1.tgz", + "integrity": "sha512-8P5M8Kxbnovd0zfvOs7ipkiVJ3/zZQ0F/nrBW4x5E+I0uAZVZ80h6CKd24fSXQ5TLK5hXMtI4yb2O5rEZdUt2A==", "dev": true }, "debug": { @@ -5806,6 +5825,11 @@ "integrity": "sha512-PzwHEmsRP3IGY4gv/Ug+rMeaTIyTJvadCb+ujYXYeIylbHJezIyNToe8KfEgHTCEYyC+/bUghYOGg8yMGlZ6vA==", "dev": true }, + "dom-align": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.0.tgz", + "integrity": "sha512-YkoezQuhp3SLFGdOlr5xkqZ640iXrnHAwVYcDg8ZKRUtO7mSzSC2BA5V0VuyAwPSJA4CLIc6EDDJh4bEsD2+zA==" + }, "dom-converter": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", @@ -5816,9 +5840,9 @@ } }, "dom-serializer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.2.0.tgz", - "integrity": "sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.1.tgz", + "integrity": "sha512-Pv2ZluG5ife96udGgEDovOOOA5UELkltfJpnIExPrAk1LTvecolUGn6lIaoLh86d83GiB86CjzciMd9BuRB71Q==", "dev": true, "requires": { "domelementtype": "^2.0.1", @@ -5833,9 +5857,9 @@ "dev": true }, "domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", "dev": true }, "domexception": { @@ -5848,23 +5872,23 @@ } }, "domhandler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.0.0.tgz", - "integrity": "sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.1.0.tgz", + "integrity": "sha512-/6/kmsGlMY4Tup/nGVutdrK9yQi4YjWVcVeoQmixpzjOUK1U7pQkvAPHBJeUxOgxF0J8f8lwCJSlCfD0V4CMGQ==", "dev": true, "requires": { - "domelementtype": "^2.1.0" + "domelementtype": "^2.2.0" } }, "domutils": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.0.tgz", - "integrity": "sha512-Ho16rzNMOFk2fPwChGh3D2D9OEHAfG19HgmRR2l+WLSsIstNsAYBzePH412bL0y5T44ejABIVfTHQ8nqi/tBCg==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.5.2.tgz", + "integrity": "sha512-MHTthCb1zj8f1GVfRpeZUbohQf/HdBos0oX5gZcQFepOZPLLRyj6Wn7XS7EMnY7CVpwv8863u2vyE83Hfu28HQ==", "dev": true, "requires": { "dom-serializer": "^1.0.1", - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0" + "domelementtype": "^2.2.0", + "domhandler": "^4.1.0" } }, "dot-case": { @@ -5917,9 +5941,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.701", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.701.tgz", - "integrity": "sha512-Zd9ofdIMYHYhG1gvnejQDvC/kqSeXQvtXF0yRURGxgwGqDZm9F9Fm3dYFnm5gyuA7xpXfBlzVLN1sz0FjxpKfw==" + "version": "1.3.712", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.712.tgz", + "integrity": "sha512-3kRVibBeCM4vsgoHHGKHmPocLqtFAGTrebXxxtgKs87hNUzXrX2NuS3jnBys7IozCnw7viQlozxKkmty2KNfrw==" }, "elliptic": { "version": "6.5.4", @@ -6583,13 +6607,13 @@ "dev": true }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -7748,9 +7772,9 @@ "dev": true }, "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, "hpack.js": { @@ -7863,9 +7887,9 @@ }, "dependencies": { "domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", "dev": true }, "entities": { @@ -8427,9 +8451,9 @@ } }, "is-docker": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz", - "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", "dev": true, "optional": true }, @@ -9014,13 +9038,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "p-locate": { @@ -9191,9 +9215,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -9390,13 +9414,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -9748,13 +9772,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -9913,13 +9937,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -10151,13 +10175,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -10578,9 +10602,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -10719,13 +10743,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -10873,9 +10897,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -11102,13 +11126,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "normalize-path": { @@ -11372,13 +11396,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -11647,13 +11671,13 @@ } }, "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", "dev": true, "requires": { "braces": "^3.0.1", - "picomatch": "^2.0.5" + "picomatch": "^2.2.3" } }, "slash": { @@ -11703,6 +11727,18 @@ } } }, + "joi": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.4.0.tgz", + "integrity": "sha512-F4WiW2xaV6wc1jxete70Rw4V/VuMd6IN+a5ilZsxG4uYtUXWu2kq9W5P2dz30e7Gmw8RCbY/u/uk+dMPma9tAg==", + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.0", + "@sideway/formula": "^3.0.0", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-base64": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", @@ -12379,16 +12415,16 @@ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.46.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", - "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.47.0.tgz", + "integrity": "sha512-QBmA/G2y+IfeS4oktet3qRZ+P5kPhCKRXxXnQEudYqUaEioAU1/Lq2us3D/t1Jfo4hE9REQPrbB7K5sOczJVIw==" }, "mime-types": { - "version": "2.1.29", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", - "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", + "version": "2.1.30", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.30.tgz", + "integrity": "sha512-crmjA4bLtR8m9qLpHvgxSChT+XoSlZi8J4n/aIdn3z92e/U47Z0V/yl+Wh9W046GgFVAmoNR/fmdbZYcSSIUeg==", "requires": { - "mime-db": "1.46.0" + "mime-db": "1.47.0" } }, "mimic-fn": { @@ -13364,9 +13400,9 @@ } }, "pbkdf2": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", - "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", "dev": true, "requires": { "create-hash": "^1.1.2", @@ -13379,13 +13415,12 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", + "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", "dev": true }, "pify": { @@ -13725,13 +13760,13 @@ }, "dependencies": { "postcss": { - "version": "8.2.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.8.tgz", - "integrity": "sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==", + "version": "8.2.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.10.tgz", + "integrity": "sha512-b/h7CPV7QEdrqIxtAf2j31U5ef05uBDuvoXv6L51Q4rcS1jdlXAVKJv+atCFdUXYl9dyTHGyoMzIepwowRJjFw==", "dev": true, "requires": { "colorette": "^1.2.2", - "nanoid": "^3.1.20", + "nanoid": "^3.1.22", "source-map": "^0.6.1" } } @@ -13992,9 +14027,9 @@ "dev": true }, "prompts": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz", - "integrity": "sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.1.tgz", + "integrity": "sha512-EQyfIuO2hPDsX1L/blblV+H7I0knhgAd82cVneCwcdND9B8AuCDuRcBH6yIcG4dFzlOUqbazQqwGjx5xmsNLuQ==", "dev": true, "requires": { "kleur": "^3.0.3", @@ -14119,6 +14154,14 @@ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", "dev": true }, + "raf": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", + "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", + "requires": { + "performance-now": "^2.1.0" + } + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -14154,6 +14197,61 @@ "unpipe": "1.0.0" } }, + "rc-align": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.9.tgz", + "integrity": "sha512-myAM2R4qoB6LqBul0leaqY8gFaiECDJ3MtQDmzDo9xM9NRT/04TvWOYd2YHU9zvGzqk9QXF6S9/MifzSKDZeMw==", + "requires": { + "@babel/runtime": "^7.10.1", + "classnames": "2.x", + "dom-align": "^1.7.0", + "rc-util": "^5.3.0", + "resize-observer-polyfill": "^1.5.1" + } + }, + "rc-motion": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-1.1.2.tgz", + "integrity": "sha512-YC/E7SSWKBFakYg4PENhSRWD4ZLDqkI7FKmutJcrMewZ91/ZIWfoZSDvPaBdKO0hsFrrzWepFhXQIq0FNnCMWA==", + "requires": { + "@babel/runtime": "^7.11.1", + "classnames": "^2.2.1", + "raf": "^3.4.1", + "rc-util": "^5.0.6" + } + }, + "rc-tooltip": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-4.2.3.tgz", + "integrity": "sha512-7ySkaPGeqLLM4a/QYrKQ280aDthPxyvjJqQMstWX/AWX7/b1p23HIdHXdjBkziuvcnvXkW4lgZdFTVsylDiX1w==", + "requires": { + "@babel/runtime": "^7.11.2", + "rc-trigger": "^4.2.1" + } + }, + "rc-trigger": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-4.4.3.tgz", + "integrity": "sha512-yq/WyuiPwxd2q6jy+VPyy0GUCRFJ2eFqAaCwPE27AOftXeIupOcJ/2t1wakSq63cfk7qtzev5DKHUAjb8LOJCw==", + "requires": { + "@babel/runtime": "^7.11.2", + "classnames": "^2.2.6", + "raf": "^3.4.1", + "rc-align": "^4.0.0", + "rc-motion": "^1.0.0", + "rc-util": "^5.0.1" + } + }, + "rc-util": { + "version": "5.9.8", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.9.8.tgz", + "integrity": "sha512-typLSHYGf5irvGLYQshs0Ra3aze086h0FhzsAkyirMunYZ7b3Te8gKa5PVaanoHaZa9sS6qx98BxgysoRP+6Tw==", + "requires": { + "@babel/runtime": "^7.12.5", + "react-is": "^16.12.0", + "shallowequal": "^1.1.0" + } + }, "react": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", @@ -14517,9 +14615,9 @@ }, "dependencies": { "domelementtype": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz", - "integrity": "sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.2.0.tgz", + "integrity": "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==", "dev": true } } @@ -14552,9 +14650,9 @@ } }, "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.4.tgz", + "integrity": "sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==", "dev": true }, "repeat-string": { @@ -14904,9 +15002,9 @@ "dev": true }, "rtl-css-js": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.0.tgz", - "integrity": "sha512-Dl5xDTeN3e7scU1cWX8c9b6/Nqz3u/HgR4gePc1kWXYiQWVQbKCEyK6+Hxve9LbcJ5EieHy1J9nJCN3grTtGwg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.14.1.tgz", + "integrity": "sha512-G9N1s/6329FpJr8k9e1U/Lg0IDWThv99sb7k0IrXHjSnubxe01h52/ajsPRafJK1/2Vqrhz3VKLe3E1dx6jS9Q==", "requires": { "@babel/runtime": "^7.1.2" } @@ -15255,6 +15353,11 @@ "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" }, + "shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -15681,9 +15784,9 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -15704,9 +15807,9 @@ } }, "stack-utils": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.4.tgz", - "integrity": "sha512-IPDJfugEGbfizBwBZRZ3xpccMdRyP5lqsBWXGQWimVjua/ccLCeMOAVjlc1R7LxFjo5sEDhyNIXd8mo/AiDS9w==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", + "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -16012,9 +16115,9 @@ } }, "stylis": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.9.tgz", - "integrity": "sha512-ci7pEFNVW3YJiWEzqPOMsAjY6kgraZ3ZgBfQ5HYbNtLJEsQ0G46ejWZpfSSCp/FaSiCSGGhzL9O2lN+2cB6ong==" + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.10.tgz", + "integrity": "sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==" }, "supports-color": { "version": "5.5.0", @@ -16025,9 +16128,9 @@ } }, "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", "dev": true, "requires": { "has-flag": "^4.0.0", @@ -16494,9 +16597,9 @@ "integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==" }, "tslib": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz", - "integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz", + "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==" }, "tty-browserify": { "version": "0.0.0", @@ -17702,9 +17805,9 @@ "dev": true }, "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yallist": { diff --git a/package.json b/package.json index 4447e89..17b3657 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@babel/plugin-transform-runtime": "^7.8.3", "@babel/preset-env": "^7.7.6", "@babel/preset-react": "^7.7.4", - "@babel/runtime": "^7.8.7", "@testing-library/jest-dom": "^5.5.0", "@testing-library/react": "^9.4.0", "@types/jest": "^25.2.1", @@ -59,13 +58,16 @@ "webpack-merge": "^4.2.2" }, "dependencies": { + "@babel/runtime": "^7.13.10", "@reach/router": "^1.3.4", "autoprefixer": "^8.6.5", "express": "^4.17.1", + "joi": "^17.4.0", "lodash": "^4.17.21", "moment": "^2.29.1", "moment-duration-format": "^2.3.2", "qs": "^6.10.1", + "rc-tooltip": "^4.2.3", "react": "^16.12.0", "react-date-range": "^1.1.3", "react-dom": "^16.12.0", diff --git a/src/App.jsx b/src/App.jsx index 51007c8..7d6a741 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,40 +1,33 @@ /** * Main App component */ -import React, { useState, useEffect, useLayoutEffect } from "react"; -import { Router, useNavigate } from "@reach/router"; +import React, { useState, useLayoutEffect, useEffect, useRef } from "react"; +import { Router, useLocation } from "@reach/router"; import Challenges from "./containers/Challenges"; import Filter from "./containers/Filter"; import Menu from "./components/Menu"; import { disableSidebarForRoute } from "@topcoder/micro-frontends-navbar-app"; -import AuthDemo from "./components/AuthDemo"; import NoSidebarDemo from "./components/NoSidebarDemo"; import * as constants from "./constants"; +import actions from "./actions"; import * as utils from "./utils"; +import store from "./store"; +import { initialChallengeFilter } from "./reducers/filter"; +import _ from "lodash"; -import "./styles/react-date-range/default.scss"; -import "./styles/react-date-range/styles.scss"; +import "react-date-range/dist/theme/default.css"; +import "react-date-range/dist/styles.css"; +import "rc-tooltip/assets/bootstrap.css"; import "./styles/main.scss"; const App = () => { const [selectedMenuItem, setSelectedMenuItem] = useState(null); - const navigateTo = useNavigate(); useLayoutEffect(() => { disableSidebarForRoute("/earn/*"); }, []); - useEffect(() => { - const path = utils.menu.getMenuItemUrlPath( - constants.NAV_MENU, - selectedMenuItem - ); - if (path) { - navigateTo(path); - } - }, [selectedMenuItem]); - const menu = ( { /> ); + const location = useLocation(); + + const getChallengesDebounced = useRef(_.debounce((f) => f(), 500)); + + useEffect(() => { + if (!location.search) { + store.dispatch(actions.challenges.getChallenges(initialChallengeFilter)); + return; + } + + if (location.pathname === "/earn/find/challenges") { + const params = utils.url.parseUrlQuery(location.search); + const toUpdate = utils.challenge.createChallengeFilter(params); + + if (!toUpdate.types) toUpdate.types = []; + if (!toUpdate.tracks) toUpdate.tracks = []; + if (!toUpdate.bucket) toUpdate.bucket = ""; + + const updatedFilter = { ...initialChallengeFilter, ...toUpdate }; + const currentFilter = store.getState().filter.challenge; + const diff = !_.isEqual(updatedFilter, currentFilter); + if (diff) { + store.dispatch(actions.filter.updateFilter(updatedFilter)); + } + getChallengesDebounced.current(() => + store.dispatch(actions.challenges.getChallenges(updatedFilter)) + ); + } + }, [location]); + + useEffect(() => { + const name = utils.menu.getNameByPath( + constants.NAV_MENU, + location.pathname + ); + if (name) { + setSelectedMenuItem(name); + } else { + setSelectedMenuItem(null); + } + }, [location]); + return (
  • - +
  • {displayPages.map((p) => (
  • @@ -106,8 +118,17 @@ const Pagination = ({ length, pageIndex, pageSize, onChange }) => {
  • ))} -
  • - +
  • +
diff --git a/src/components/Pagination/styles.scss b/src/components/Pagination/styles.scss index edbcfa6..0e4f979 100644 --- a/src/components/Pagination/styles.scss +++ b/src/components/Pagination/styles.scss @@ -12,8 +12,7 @@ :global { .Select-value-label::after { - content: 'per page'; - margin-left: 0.15em; + content: ' per page'; } } } @@ -59,6 +58,22 @@ &.hidden { visibility: hidden; } + + .arrow { + display: inline-block; + } + } + + &.previous { + .arrow { + transform: rotate(90deg) scale(0.75); + } + } + + &.next { + .arrow { + transform: rotate(-90deg) scale(0.75); + } } } diff --git a/src/components/RadioButton/index.jsx b/src/components/RadioButton/index.jsx index ba9e84b..e48eb69 100644 --- a/src/components/RadioButton/index.jsx +++ b/src/components/RadioButton/index.jsx @@ -1,7 +1,7 @@ /** * Radio button component. */ -import React, { useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import PT from "prop-types"; import _ from "lodash"; import "./styles.scss"; @@ -20,6 +20,10 @@ function RadioButton({ options, onChange, size, errorMsg }) { _.debounce((q, cb) => cb(q), process.env.GUIKIT.DEBOUNCE_ON_CHANGE_TIME) // eslint-disable-line no-undef ).current; + useEffect(() => { + setInternalOptions(options); + }, [options]); + return (
( - onClick(tag)}> + { + onClick(tag); + }} + > {tag} - + ); Tag.propTypes = { diff --git a/src/components/Tag/styles.scss b/src/components/Tag/styles.scss index d0c3692..9102d4f 100644 --- a/src/components/Tag/styles.scss +++ b/src/components/Tag/styles.scss @@ -9,4 +9,5 @@ white-space: nowrap; border: 1px solid $body-color; border-radius: $border-radius-sm; + cursor: pointer; } diff --git a/src/components/TextInput/index.jsx b/src/components/TextInput/index.jsx index 55735a8..a01ebb5 100644 --- a/src/components/TextInput/index.jsx +++ b/src/components/TextInput/index.jsx @@ -59,7 +59,7 @@ function TextInput({ }} /> {label ? ( -
- + + + + +
); diff --git a/src/containers/Challenges/Listing/ChallengeItem/styles.scss b/src/containers/Challenges/Listing/ChallengeItem/styles.scss index 693f532..8039ea4 100644 --- a/src/containers/Challenges/Listing/ChallengeItem/styles.scss +++ b/src/containers/Challenges/Listing/ChallengeItem/styles.scss @@ -35,8 +35,14 @@ } .tags { - max-width: calc(50% - 32px); + max-width: calc(50% - 84px); + min-width: calc(50% - 84px); flex: 1 1 auto; + + @media (min-width: $screen-xxl + 1px) { + min-width: 294px; + max-width: 25%; + } } .nums { @@ -44,7 +50,15 @@ white-space: nowrap; > * { - margin: 0 16px; + margin: 0 20px; + + &:first-child { + margin-left: 0; + } + + &:last-child { + margin-right: 0; + } } } } diff --git a/src/containers/Challenges/Listing/ChallengeError/index.jsx b/src/containers/Challenges/Listing/errors/ChallengeError/index.jsx similarity index 96% rename from src/containers/Challenges/Listing/ChallengeError/index.jsx rename to src/containers/Challenges/Listing/errors/ChallengeError/index.jsx index f0b62c8..a9a2307 100644 --- a/src/containers/Challenges/Listing/ChallengeError/index.jsx +++ b/src/containers/Challenges/Listing/errors/ChallengeError/index.jsx @@ -9,7 +9,7 @@ const ChallengeError = () => ( not found

- No challenges were found. You can try changing your search perimeters. + No challenges were found. You can try changing your search parameters.

); diff --git a/src/containers/Challenges/Listing/ChallengeError/styles.scss b/src/containers/Challenges/Listing/errors/ChallengeError/styles.scss similarity index 100% rename from src/containers/Challenges/Listing/ChallengeError/styles.scss rename to src/containers/Challenges/Listing/errors/ChallengeError/styles.scss diff --git a/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/index.jsx b/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/index.jsx new file mode 100644 index 0000000..bc7fa3e --- /dev/null +++ b/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/index.jsx @@ -0,0 +1,18 @@ +import React from "react"; +import IconNotFound from "assets/icons/not-found-recommended.png"; +import "./styles.scss"; + +const ChallengeRecommendedError = () => ( +
+

+ not found +

+

+ Looks like there are no Recommended Challenges that best + match your skills at this point. But you can try to join other challenges + that work for you. +

+
+); + +export default ChallengeRecommendedError; diff --git a/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/styles.scss b/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/styles.scss new file mode 100644 index 0000000..a1efc68 --- /dev/null +++ b/src/containers/Challenges/Listing/errors/ChallengeRecommendedError/styles.scss @@ -0,0 +1,24 @@ +@import "styles/variables"; + +.challenge-recommended-error { + padding: 16px 24px; + min-height: 136px; + margin-bottom: 35px; + font-size: $font-size-sm; + line-height: 22px; + text-align: center; + background: $white; + border-radius: $border-radius-lg; + + h1 { + padding: 15px 0 10px; + } + + p { + margin-bottom: 20px; + } + + strong { + font-weight: bold; + } +} diff --git a/src/containers/Challenges/Listing/index.jsx b/src/containers/Challenges/Listing/index.jsx index 7c18893..a0c02de 100644 --- a/src/containers/Challenges/Listing/index.jsx +++ b/src/containers/Challenges/Listing/index.jsx @@ -16,6 +16,7 @@ import "./styles.scss"; const Listing = ({ challenges, + search, page, perPage, sortBy, @@ -24,10 +25,10 @@ const Listing = ({ startDateEnd, updateFilter, bucket, - getChallenges, + sortByLabels, }) => { const sortByOptions = utils.createDropdownOptions( - Object.keys(constants.CHALLENGE_SORT_BY), + sortByLabels, utils.getSortByLabel(constants.CHALLENGE_SORT_BY, sortBy) ); @@ -42,13 +43,13 @@ const Listing = ({ search { onSearch.current(() => { const filterChange = { search: value }; updateFilter(filterChange); - getChallenges(filterChange); }); }} /> @@ -64,14 +65,13 @@ const Listing = ({ options={sortByOptions} size="xs" onChange={(newSortByOptions) => { - const selectOption = utils.getSelectedDropdownOption( + const selectedOption = utils.getSelectedDropdownOption( newSortByOptions ); const filterChange = { - sortBy: constants.CHALLENGE_SORT_BY[selectOption.label], + sortBy: constants.CHALLENGE_SORT_BY[selectedOption.label], }; updateFilter(filterChange); - getChallenges(filterChange); }} /> @@ -90,7 +90,6 @@ const Listing = ({ : null; const filterChange = { endDateStart: s, startDateEnd: d }; updateFilter(filterChange); - getChallenges(filterChange); }} range={{ startDate: endDateStart ? moment(endDateStart).toDate() : null, @@ -108,12 +107,10 @@ const Listing = ({ onClickTag={(tag) => { const filterChange = { search: tag }; updateFilter(filterChange); - getChallenges(filterChange); }} onClickTrack={(track) => { const filterChange = { tracks: [track] }; updateFilter(filterChange); - getChallenges(filterChange); }} /> @@ -129,7 +126,6 @@ const Listing = ({ perPage: event.pageSize, }; updateFilter(filterChange); - getChallenges(filterChange); }} /> @@ -140,15 +136,16 @@ const Listing = ({ Listing.propTypes = { challenges: PT.arrayOf(PT.shape()), + search: PT.string, page: PT.number, perPage: PT.number, sortBy: PT.string, total: PT.number, endDateStart: PT.string, startDateEnd: PT.string, - getChallenges: PT.func, updateFilter: PT.func, bucket: PT.string, + sortByLabels: PT.arrayOf(PT.string), }; export default Listing; diff --git a/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/index.jsx b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/index.jsx new file mode 100644 index 0000000..3e1b858 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/index.jsx @@ -0,0 +1,29 @@ +import React from "react"; +import PT from "prop-types"; +import * as utils from "../../../../../../utils"; + +import "./styles.scss"; + +const Prize = ({ place, prize, currencySymbol }) => ( +
3 ? "nth" : place}`}> + + {place} + + + {utils.formatMoneyValue(prize, currencySymbol)} + +
+); + +Prize.defaultProps = { + place: 1, +}; + +Prize.propTypes = {}; + +export default Prize; diff --git a/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/styles.scss b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/styles.scss new file mode 100644 index 0000000..df9e549 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/Prize/styles.scss @@ -0,0 +1,52 @@ +@import "styles/variables"; +@import "styles/mixins"; + +.medal { + &.place-1 { + color: $tc-gold; + } + + &.place-2 { + color: $tc-silver; + } + + &.place-3 { + color: $tc-bronze; + } + + &.place-nth { + color: $tc-dark-blue-30; + } +} + +.prize { + min-width: 24px; + margin-right: 24px; + padding-bottom: 10px; + box-shadow: inset 0 -3px currentColor; + + .value, + .placement { + display: block; + } + + .placement { + @include roboto-bold; + + font-size: 10px; + line-height: 14px; + letter-spacing: 0.42px; + + &::after { + content: attr(data-placement); + } + } + + .value { + @include barlow-semibold; + + font-size: 20px; + line-height: 24px; + color: $white; + } +} diff --git a/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/index.jsx b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/index.jsx new file mode 100644 index 0000000..e3ebf77 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/index.jsx @@ -0,0 +1,57 @@ +import React from "react"; +import PT from "prop-types"; +import Tooltip from "../../../../../components/Tooltip"; +import Prize from "./Prize"; +import * as utils from "../../../../../utils"; + +import "./styles.scss"; + +const PlacementsTooltip = ({ + children, + prizes, + checkpointPrizes, + currencySymbol, + placement, +}) => { + let numberOfCheckpointsPrizes; + let topCheckPointPrize; + if (checkpointPrizes && checkpointPrizes.length) { + numberOfCheckpointsPrizes = checkpointPrizes.length; + topCheckPointPrize = checkpointPrizes[0]; + } + + const Content = () => ( +
+
    + {prizes.map((prize, index) => ( +
  • + +
  • + ))} +
+ {checkpointPrizes && checkpointPrizes.length > 0 && ( +

+ {numberOfCheckpointsPrizes} checkpoints awarded worth{" "} + + {utils.formatMoneyValue(topCheckPointPrize, currencySymbol)} + {" "} + each +

+ )} +
+ ); + + return ( + }> + {children} + + ); +}; + +PlacementsTooltip.propTypes = {}; + +export default PlacementsTooltip; diff --git a/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/styles.scss b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/styles.scss new file mode 100644 index 0000000..ef89f1a --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/PlacementsTooltip/styles.scss @@ -0,0 +1,23 @@ +@import "styles/variables"; +@import "styles/mixins"; + +$prize-space-10: $base-unit * 2; + +.prizes-tooltip { + max-width: 480px; + padding: 15px; + overflow: hidden; + + .placements { + display: flex; + } + + .checkpoint-message { + margin-top: 14px; + font-weight: bold; + font-size: 12px; + line-height: 16px; + letter-spacing: 0.25px; + color: $white; + } +} diff --git a/src/containers/Challenges/Listing/tooltips/ProgressTooltip/index.jsx b/src/containers/Challenges/Listing/tooltips/ProgressTooltip/index.jsx new file mode 100644 index 0000000..3b94d1a --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/ProgressTooltip/index.jsx @@ -0,0 +1,142 @@ +import React from "react"; +import PT from "prop-types"; +import Tooltip from "../../../../../components/Tooltip"; +import * as util from "../../../../../utils/challenge"; +import _ from "lodash"; +import moment from "moment"; + +import "./styles.scss"; + +const ProgressTooltip = ({ children, challenge, placement }) => { + const Phase = ({ date, last, phase, progress, started }) => { + const limitProgress = parseFloat(_.replace(progress, "%", "")); + const limitWidth = limitProgress <= 100 ? limitProgress : 100; + + return ( +
+
{phase}
+
+
+
+
+
{`${getDate(date)}, ${getTime(date)}`}
+
+ ); + }; + + const Content = ({ c }) => { + let steps = []; + + const allPhases = c.phases || []; + const endPhaseDate = Math.max( + ...allPhases.map((p) => util.getChallengePhaseEndDate(p)) + ); + const registrationPhase = + allPhases.find((phase) => phase.name === "Registration") || {}; + const submissionPhase = + allPhases.find((phase) => phase.name === "Submission") || {}; + const checkpointPhase = + allPhases.find((phase) => phase.name === "Checkpoint Submission") || {}; + + if (!_.isEmpty(registrationPhase)) { + steps.push({ + date: util.getChallengePhaseStartDate(registrationPhase), + name: "Start", + }); + } + if (!_.isEmpty(checkpointPhase)) { + steps.push({ + date: util.getChallengePhaseEndDate(checkpointPhase), + name: "Checkpoint", + }); + } + const iterativeReviewPhase = allPhases.find( + (phase) => phase.isOpen && phase.name === "Iterative Review" + ); + if (iterativeReviewPhase) { + steps.push({ + date: util.getChallengePhaseEndDate(iterativeReviewPhase), + name: "Iterative Review", + }); + } else if (!_.isEmpty(submissionPhase)) { + steps.push({ + date: util.getChallengePhaseEndDate(submissionPhase), + name: "Submission", + }); + } + steps.push({ + date: new Date(endPhaseDate), + name: "End", + }); + + steps = steps.sort((a, b) => a.date.getTime() - b.date.getTime()); + const currentPhaseEnd = new Date(); + steps = steps.map((step, index) => { + let progress = 0; + if (index < steps.length - 1) { + if (steps[1 + index].date.getTime() < currentPhaseEnd.getTime()) + progress = 100; + else if (step.date.getTime() > currentPhaseEnd.getTime()) progress = 0; + else { + const left = currentPhaseEnd.getTime() - step.date.getTime(); + if (left < 0) progress = -1; + else { + progress = + 100 * + (left / + (steps[1 + index].date.getTime() - + steps[index].date.getTime())); + } + } + } + + const phaseId = index; + return ( + + ); + }); + + return ( +
+
{steps}
+
+ ); + }; + + return ( + }> + {children} + + ); +}; + +ProgressTooltip.defaultProps = { + placement: "bottom", +}; + +ProgressTooltip.propTypes = {}; + +export default ProgressTooltip; + +function getDate(date) { + return moment(date).format("MMM DD"); +} + +function getTime(date) { + const duration = moment(date); + const hour = duration.hours(); + const hString = hour < 10 ? `0${hour}` : hour; + const min = duration.minutes(); + const mString = min < 10 ? `0${min}` : min; + const res = `${hString}:${mString}`; + return res[1] === "-" ? "Late" : `${res}`; +} diff --git a/src/containers/Challenges/Listing/tooltips/ProgressTooltip/styles.scss b/src/containers/Challenges/Listing/tooltips/ProgressTooltip/styles.scss new file mode 100644 index 0000000..24dd331 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/ProgressTooltip/styles.scss @@ -0,0 +1,89 @@ +@import 'styles/variables'; +@import 'styles/mixins'; + +$corner-radius: 2px; +$tip-offset: 80px; +$tip-space-10: $base-unit * 2; +$tip-space-15: $base-unit * 3; +$tip-space-35: $base-unit * 7; +$tip-space-95: $base-unit * 19; +$tip-radius-15: $corner-radius * 7 + 1; +$tip-radius-4: $corner-radius * 2; + +div.progress-bar-tooltip { + @include roboto-regular; + + color: $tc-white; + max-width: none; + padding: 0 $base-unit * 3; + + .rc-tooltip-inner { + padding: 0 $base-unit * 3; + } + + .tip { + word-wrap: none; + white-space: nowrap; + + .phase { + display: inline-block; + line-height: $tip-space-15; + min-width: $tip-offset; + padding: 18px 0; + width: $tip-space-95; + + &:last-child { + border-radius: 0 $tip-radius-4 $tip-radius-4 0; + padding-right: $tip-space-15; + } + } + } + + .bar { + background: $tc-gray-70; + height: 8px; + margin: ($tip-space-10) - 3 0; + width: 100%; + + &.last { + width: 0; + } + + .inner-bar { + background: $lightGreen; + position: relative; + border-radius: $corner-radius * 4; + border: 2px solid $tc-gray-90; + top: -16px; + height: $tip-space-10 - 1; + z-index: 1; + } + + .point { + background: $tc-gray-20; + border: 2px solid $tc-gray-90; + border-radius: $corner-radius * 9; + height: 16px; + left: -($base-unit - 2); + position: relative; + top: -4px; + width: 16px; + z-index: 2; + } + + &.started { + .point { + background: $lightGreen; + } + } + } + + .date { + font-weight: 400; + font-size: 12px; + color: $tc-gray-30; + letter-spacing: 0; + line-height: $tip-space-15; + text-shadow: none; + } +} diff --git a/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/index.jsx b/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/index.jsx new file mode 100644 index 0000000..e7df0e8 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/index.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import PT from "prop-types"; +import Tooltip from "../../../../../components/Tooltip"; +import Tag from "../../../../../components/Tag"; + +import "./styles.scss"; + +const TagsMoreTooltip = ({ children, tags, onClickTag, placement }) => { + const Content = () => { + return ( +
+ {tags.map((tag) => ( + + ))} +
+ ); + }; + + return ( + } + trigger={["hover", "focus"]} + > + {children} + + ); +}; + +TagsMoreTooltip.defaultProps = { + placement: "bottom", +}; + +TagsMoreTooltip.propTypes = {}; + +export default TagsMoreTooltip; diff --git a/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/styles.scss b/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/styles.scss new file mode 100644 index 0000000..afe51e8 --- /dev/null +++ b/src/containers/Challenges/Listing/tooltips/TagsMoreTooltip/styles.scss @@ -0,0 +1,17 @@ +@import "styles/variables"; + +.tags-more { + padding: 10px 6px 6px 10px; + max-width: 206px; + + > * { + margin-right: 4px; + margin-bottom: 4px; + padding: 6px 4px; + font-size: 11px; + line-height: 15px; + letter-spacing: 0.5px; + color: $white; + background: $tc-gray-70; + } +} diff --git a/src/containers/Challenges/index.jsx b/src/containers/Challenges/index.jsx index 9a890c0..f6a63ea 100644 --- a/src/containers/Challenges/index.jsx +++ b/src/containers/Challenges/index.jsx @@ -3,45 +3,81 @@ import PT from "prop-types"; import { connect } from "react-redux"; import Listing from "./Listing"; import actions from "../../actions"; -import ChallengeError from "./Listing/ChallengeError"; +import ChallengeError from "./Listing/errors/ChallengeError"; +import ChallengeRecommendedError from "./Listing/errors/ChallengeRecommendedError"; +import * as constants from "../../constants"; +import IconListView from "../../assets/icons/list-view.svg"; +import IconCardView from "../../assets/icons/card-view.svg"; + import "./styles.scss"; const Challenges = ({ challenges, + search, page, perPage, sortBy, total, endDateStart, startDateEnd, - getChallenges, updateFilter, bucket, + recommended, + recommendedChallenges, + initialized, + updateQuery, }) => { - const [initialized, setInitialized] = useState(false); - - useEffect(() => { - getChallenges().finally(() => setInitialized(true)); - }, []); + const BUCKET_OPEN_FOR_REGISTRATION = constants.FILTER_BUCKETS[1]; + const isRecommended = recommended && bucket === BUCKET_OPEN_FOR_REGISTRATION; + const sortByValue = isRecommended + ? sortBy + : sortBy === constants.CHALLENGE_SORT_BY_RECOMMENDED + ? constants.CHALLENGE_SORT_BY_MOST_RECENT + : sortBy; + const sortByLabels = isRecommended + ? Object.keys(constants.CHALLENGE_SORT_BY) + : Object.keys(constants.CHALLENGE_SORT_BY).filter( + (label) => label !== constants.CHALLENGE_SORT_BY_RECOMMENDED_LABEL + ); + const noRecommendedChallenges = + bucket === BUCKET_OPEN_FOR_REGISTRATION && + recommended && + recommendedChallenges.length === 0; return (
-

CHALLENGES

- {challenges.length === 0 ? ( - initialized && - ) : ( - +

+ CHALLENGES + + + + +

+ {challenges.length === 0 && initialized && } + {challenges.length > 0 && ( + <> + {noRecommendedChallenges && } + { + updateFilter(filterChange); + updateQuery(filterChange); + }} + bucket={bucket} + sortByLabels={sortByLabels} + /> + )}
); @@ -49,19 +85,24 @@ const Challenges = ({ Challenges.propTypes = { challenges: PT.arrayOf(PT.shape()), + search: PT.string, page: PT.number, perPage: PT.number, sortBy: PT.string, total: PT.number, endDateStart: PT.string, startDateEnd: PT.string, - getChallenges: PT.func, updateFilter: PT.func, bucket: PT.string, + recommended: PT.bool, + recommendedChallenges: PT.arrayOf(PT.shape()), + initialized: PT.bool, + updateQuery: PT.func, }; const mapStateToProps = (state) => ({ state: state, + search: state.filter.challenge.search, page: state.filter.challenge.page, perPage: state.filter.challenge.perPage, sortBy: state.filter.challenge.sortBy, @@ -70,20 +111,26 @@ const mapStateToProps = (state) => ({ startDateEnd: state.filter.challenge.startDateEnd, challenges: state.challenges.challengesFiltered, bucket: state.filter.challenge.bucket, + recommended: state.filter.challenge.recommended, + recommendedChallenges: state.challenges.recommendedChallenges, + initialized: state.challenges.initialized, }); const mapDispatchToProps = { - getChallenges: actions.challenges.getChallenges, updateFilter: actions.filter.updateFilter, + updateQuery: actions.filter.updateChallengeQuery, }; const mergeProps = (stateProps, dispatchProps, ownProps) => ({ ...ownProps, ...stateProps, ...dispatchProps, - getChallenges: (change) => - dispatchProps.getChallenges( - { ...stateProps.state.filter.challenge, ...change }, + updateQuery: (change) => + dispatchProps.updateQuery( + { + ...stateProps.state.filter.challenge, + ...change, + }, change ), }); diff --git a/src/containers/Challenges/styles.scss b/src/containers/Challenges/styles.scss index b43a724..13c7fde 100644 --- a/src/containers/Challenges/styles.scss +++ b/src/containers/Challenges/styles.scss @@ -9,3 +9,30 @@ @include barlow-condensed-medium; margin-bottom: 22px; } + +.button-icon { + width: 32px; + height: 32px; + padding: 0; + line-height: 0; + text-align: center; + vertical-align: middle; + appearance: none; + background: none; + border: 0; + border-radius: 50%; + + &.active { + path { + fill: $darkGreen; + } + } +} + +.view-mode { + float: right; + + > button { + margin: 0 2px; + } +} diff --git a/src/containers/Filter/ChallengeFilter/index.jsx b/src/containers/Filter/ChallengeFilter/index.jsx index 0575912..ee45f5a 100644 --- a/src/containers/Filter/ChallengeFilter/index.jsx +++ b/src/containers/Filter/ChallengeFilter/index.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import PT from "prop-types"; import _ from "lodash"; import RadioButton from "../../../components/RadioButton"; @@ -8,6 +8,7 @@ import Toggle from "../../../components/Toggle"; import Button from "../../../components/Button"; import TextInput from "../../../components/TextInput"; import * as utils from "../../../utils"; +import * as constants from "../../../constants"; import "./styles.scss"; @@ -16,9 +17,11 @@ const ChallengeFilter = ({ types, tracks, tags, - prizeFrom, - prizeTo, - subCommunities, + totalPrizesFrom, + totalPrizesTo, + recommended, + events, + groups, challengeBuckets, challengeTypes, challengeTracks, @@ -26,36 +29,51 @@ const ChallengeFilter = ({ challengeSubCommunities, saveFilter, clearFilter, + updateFilter, + openForRegistrationCount, }) => { + const BUCKET_OPEN_FOR_REGISTRATION = constants.FILTER_BUCKETS[1]; const tagOptions = utils.createDropdownTermOptions(challengeTags); const bucketOptions = utils.createRadioOptions(challengeBuckets, bucket); - const [filter, setFilter] = useState( - _.cloneDeep({ - bucket, - types, - tracks, - tags, - prizeFrom, - prizeTo, - subCommunities, - }) - ); + const caseSensitive = false; + utils.setSelectedDropdownTermOptions(tagOptions, tags, caseSensitive); + + const ref = useRef(null); + + useEffect(() => { + if (!ref.current) { + return; + } + + const openForRegistrationElement = ref.current.children[0].children[1]; + const badgeElement = utils.icon.createBadgeElement( + openForRegistrationElement, + `${openForRegistrationCount}` + ); - utils.setSelectedDropdownTermOptions(tagOptions, filter.tags); + return () => { + badgeElement.parentElement.removeChild(badgeElement); + }; + }, [openForRegistrationCount]); + + const [totalPrizesFromError, setTotalPrizesFromError] = useState(null); + const [totalPrizesToError, setTotalPrizesToError] = useState(null); + + const onInputTotalPrizesFrom = useRef(_.debounce((f) => f(), 500)); + const onInputTotalPrizesTo = useRef(_.debounce((f) => f(), 500)); return (
-
+
{ const filterChange = { - ...filter, bucket: utils.getSelectedRadioOption(newBucketOptions).label, + page: 1, }; - setFilter(filterChange); - saveFilter(filterChange); + updateFilter(filterChange); }} /> @@ -67,12 +85,13 @@ const ChallengeFilter = ({ {challengeTypes.map((type) => ( { const newTypes = checked - ? filter.types.concat(type) - : filter.types.filter((i) => i !== type); - setFilter({ ...filter, types: newTypes }); + ? types.concat(type) + : types.filter((i) => i !== type); + const filterChange = { types: newTypes }; + updateFilter(filterChange); }} /> {type} @@ -87,12 +106,13 @@ const ChallengeFilter = ({ {challengeTracks.map((track) => ( { const newTracks = checked - ? filter.tracks.concat(track) - : filter.tracks.filter((i) => i !== track); - setFilter({ ...filter, tracks: newTracks }); + ? tracks.concat(track) + : tracks.filter((i) => i !== track); + const filterChange = { tracks: newTracks }; + updateFilter(filterChange); }} /> {track} @@ -110,19 +130,80 @@ const ChallengeFilter = ({ const selectedTagOptions = utils.getSelectedDropdownTermsOptions( newTagOptions ); - setFilter({ - ...filter, + const filterChange = { tags: selectedTagOptions.map((tagOption) => tagOption.label), - }); + }; + updateFilter(filterChange); }} + size="xs" />
-
+

Prize Amount

- +
+ + onInputTotalPrizesFrom.current(() => { + if (value === "") { + setTotalPrizesFromError("Required"); + return; + } + value = utils.parseTotalPrizes(value); + if (value == null) { + setTotalPrizesFromError("Invalid format"); + return; + } else { + setTotalPrizesFromError(null); + } + if (totalPrizesToError) { + return; + } + const filterChange = { + totalPrizesFrom: value, + }; + updateFilter(filterChange); + }) + } + errorMsg={totalPrizesFromError} + /> + USD +
- +
+ + onInputTotalPrizesTo.current(() => { + if (value === "") { + setTotalPrizesToError("Required"); + return; + } + value = utils.parseTotalPrizes(value); + if (value == null) { + setTotalPrizesToError("Invalid format"); + return; + } else { + setTotalPrizesToError(null); + } + if (totalPrizesFromError) { + return; + } + const filterChange = { + totalPrizesTo: value, + }; + updateFilter(filterChange); + }) + } + errorMsg={totalPrizesToError} + /> + USD +
{challengeSubCommunities.length > 0 && ( @@ -130,31 +211,78 @@ const ChallengeFilter = ({

Sub Community

{challengeSubCommunities.map((subCommunity) => ( - + { - const newSubCommunities = checked - ? filter.subCommunities.concat(subCommunity) - : filter.subCommunities.filter((i) => i !== subCommunity); - setFilter({ ...filter, subCommunities: newSubCommunities }); + const isTCOEvent = utils.challenge.isTCOEventCommunity( + subCommunity + ); + let filterChange; + + if (isTCOEvent) { + const newEvents = checked + ? events.concat( + utils.challenge.getCommunityEvent(subCommunity) + ) + : events.filter( + (event) => + event !== + utils.challenge.getCommunityEvent(subCommunity) + ); + filterChange = { events: newEvents }; + } else { + const newGroups = checked + ? groups.concat( + utils.challenge.getCommunityGroup(subCommunity) + ) + : groups.filter( + (group) => + group !== + utils.challenge.getCommunityGroup(subCommunity) + ); + filterChange = { groups: newGroups }; + } + + updateFilter(filterChange); }} /> - {subCommunity} + {subCommunity.communityName} ))}
)} -
- - - - Recommended Challenges -
+ + {bucket === BUCKET_OPEN_FOR_REGISTRATION && ( +
+ + { + updateFilter({ + recommended: checked, + sortBy: checked + ? constants.CHALLENGE_SORT_BY_RECOMMENDED + : constants.CHALLENGE_SORT_BY_MOST_RECENT, + }); + }} + /> + + Recommended Challenges +
+ )}
- +
); @@ -165,16 +293,19 @@ ChallengeFilter.propTypes = { types: PT.arrayOf(PT.string), tracks: PT.arrayOf(PT.string), tags: PT.arrayOf(PT.string), - prizeFrom: PT.number, - prizeTo: PT.number, - subCommunities: PT.arrayOf(PT.string), + totalPrizesFrom: PT.number, + totalPrizesTo: PT.number, + events: PT.arrayOf(PT.string), + groups: PT.arrayOf(PT.string), challengeBuckets: PT.arrayOf(PT.string), challengeTypes: PT.arrayOf(PT.string), challengeTracks: PT.arrayOf(PT.string), challengeTags: PT.arrayOf(PT.string), - challengeSubCommunities: PT.arrayOf(PT.string), + challengeSubCommunities: PT.arrayOf(PT.shape()), saveFilter: PT.func, clearFilter: PT.func, + updateFilter: PT.func, + openForRegistrationCount: PT.number, }; export default ChallengeFilter; diff --git a/src/containers/Filter/ChallengeFilter/styles.scss b/src/containers/Filter/ChallengeFilter/styles.scss index 1af3174..fc29ced 100644 --- a/src/containers/Filter/ChallengeFilter/styles.scss +++ b/src/containers/Filter/ChallengeFilter/styles.scss @@ -12,12 +12,12 @@ $filter-padding-y: 3 * $base-unit; .buckets, .challenge-types, .tracks, -.prize, +.total-prizes, .sub-communities { margin-bottom: 18px; > h3 { - margin-bottom: 12px; + margin-bottom: 15px; font-size: inherit; line-height: 19px; } @@ -29,6 +29,21 @@ $filter-padding-y: 3 * $base-unit; margin: $base-unit 0; } } + :global { + .count-badge { + display: inline-block; + margin: -2px 0 0 $base-unit; + padding: 2px 5px 0; + font-weight: bold; + font-size: 11px; + line-height: 14px; + text-align: center; + color: white; + vertical-align: middle; + background: #EF476F; + border-radius: 13px; + } + } } .challenge-types, @@ -51,14 +66,18 @@ $filter-padding-y: 3 * $base-unit; .skills { margin-bottom: 34px; + + @include dropdown-hide-chevron; + @include dropdown-show-label; } -.prize { +.total-prizes { + margin-top: -12px; margin-bottom: 23px; > div { display: inline-flex; - width: calc(50% - 8px); + width: calc(50% - 6px); } .separator { @@ -67,11 +86,31 @@ $filter-padding-y: 3 * $base-unit; height: 1px; margin: 0 4px; background: $body-color; + vertical-align: middle; } + + @include textinput-show-label; } -.prize { +.input-group { + position: relative; margin-top: -12px; + + input { + padding-right: 36px !important; + } + + .suffix { + @include roboto-medium; + + position: absolute; + top: 22px; + right: 10px; + font-size: 13px; + line-height: 22px; + color: $tc-gray-30; + pointer-events: none; + } } .recommended-challenges { @@ -98,3 +137,4 @@ $filter-padding-y: 3 * $base-unit; margin-left: 9px; } } + diff --git a/src/containers/Filter/index.jsx b/src/containers/Filter/index.jsx index e4717f2..82c47d0 100644 --- a/src/containers/Filter/index.jsx +++ b/src/containers/Filter/index.jsx @@ -1,53 +1,67 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import PT from "prop-types"; import { connect } from "react-redux"; import ChallengeFilter from "./ChallengeFilter"; import actions from "../../actions"; +import * as utils from "../../utils"; const Filter = ({ bucket, types, tracks, tags, - prizeFrom, - prizeTo, - subCommunities, + totalPrizesFrom, + totalPrizesTo, + recommended, + events, + groups, challengeBuckets, challengeTypes, challengeTracks, challengeTags, challengeSubCommunities, updateFilter, - getChallenges, getTags, getSubCommunities, + openForRegistrationCount, + clearFilter, + updateQuery, }) => { + const latestPropsRef = useRef(null); + latestPropsRef.current = { getTags, getSubCommunities }; + useEffect(() => { - getTags(); - getSubCommunities(); + latestPropsRef.current.getTags(); + latestPropsRef.current.getSubCommunities(); }, []); - const onSaveFilter = (filter) => { - updateFilter(filter); - getChallenges(filter); - }; - return ( {}} + saveFilter={() => {}} + clearFilter={() => { + const filterChange = utils.challenge.createEmptyChallengeFilter(); + clearFilter(filterChange); + updateQuery(filterChange); + }} + updateFilter={(filterChange) => { + updateFilter(filterChange); + updateQuery(filterChange); + }} + openForRegistrationCount={openForRegistrationCount} /> ); }; @@ -57,18 +71,23 @@ Filter.propTypes = { types: PT.arrayOf(PT.string), tracks: PT.arrayOf(PT.string), tags: PT.arrayOf(PT.string), - prizeFrom: PT.number, - prizeTo: PT.number, - subCommunities: PT.arrayOf(PT.string), + totalPrizesFrom: PT.number, + totalPrizesTo: PT.number, + recommended: PT.bool, + events: PT.arrayOf(PT.string), + groups: PT.arrayOf(PT.string), challengeBuckets: PT.arrayOf(PT.string), challengeTypes: PT.arrayOf(PT.string), challengeTracks: PT.arrayOf(PT.string), challengeTags: PT.arrayOf(PT.string), - challengeSubCommunities: PT.arrayOf(PT.string), + challengeSubCommunities: PT.arrayOf(PT.shape()), updateFilter: PT.func, - getChallenges: PT.func, getTags: PT.func, getSubCommunities: PT.func, + selectAllSubCommunities: PT.func, + openForRegistrationCount: PT.number, + clearFilter: PT.func, + updateQuery: PT.func, }; const mapStateToProps = (state) => ({ @@ -77,30 +96,37 @@ const mapStateToProps = (state) => ({ types: state.filter.challenge.types, tracks: state.filter.challenge.tracks, tags: state.filter.challenge.tags, - prizeFrom: state.filter.challenge.prizeFrom, - prizeTo: state.filter.challenge.prizeTo, - subCommunities: state.filter.challenge.subCommunities, + totalPrizesFrom: state.filter.challenge.totalPrizesFrom, + totalPrizesTo: state.filter.challenge.totalPrizesTo, + recommended: state.filter.challenge.recommended, + events: state.filter.challenge.events, + groups: state.filter.challenge.groups, challengeBuckets: state.lookup.buckets, challengeTypes: state.lookup.types, challengeTracks: state.lookup.tracks, challengeTags: state.lookup.tags, challengeSubCommunities: state.lookup.subCommunities, + openForRegistrationCount: state.challenges.openForRegistrationCount, }); const mapDispatchToProps = { updateFilter: actions.filter.updateFilter, - getChallenges: actions.challenges.getChallenges, getTags: actions.lookup.getTags, getSubCommunities: actions.lookup.getCommunityList, + clearFilter: actions.filter.clearChallengeFilter, + updateQuery: actions.filter.updateChallengeQuery, }; const mergeProps = (stateProps, dispatchProps, ownProps) => ({ ...ownProps, ...stateProps, ...dispatchProps, - getChallenges: (change) => - dispatchProps.getChallenges( - { ...stateProps.state.filter.challenge, ...change }, + updateQuery: (change) => + dispatchProps.updateQuery( + { + ...stateProps.state.filter.challenge, + ...change, + }, change ), }); diff --git a/src/reducers/challenges.js b/src/reducers/challenges.js index 827b06f..b8e9c6a 100644 --- a/src/reducers/challenges.js +++ b/src/reducers/challenges.js @@ -6,6 +6,11 @@ const defaultState = { challenges: [], challengesFiltered: [], total: 0, + loadingRecommendedChallenges: false, + loadingRecommendedChallengesError: null, + recommendedChallenges: [], + openForRegistrationCount: 0, + initialized: false, }; function onGetChallengesInit(state) { @@ -20,6 +25,8 @@ function onGetChallengesDone(state, { payload }) { challenges: payload.challenges, challengesFiltered: payload.challengesFiltered, total: payload.total, + openForRegistrationCount: payload.openForRegistrationCount, + initialized: true, }; } @@ -31,6 +38,8 @@ function onGetChallengesFailure(state, { payload }) { challenges: [], challengesFiltered: [], total: 0, + openForRegistrationCount: 0, + initialized: true, }; } diff --git a/src/reducers/filter.js b/src/reducers/filter.js index 6346dce..6250862 100644 --- a/src/reducers/filter.js +++ b/src/reducers/filter.js @@ -1,36 +1,56 @@ import { handleActions } from "redux-actions"; import * as constants from "../constants"; +import _ from "lodash"; const defaultState = { challenge: { types: constants.FILTER_CHALLENGE_TYPES, - tracks: constants.FILTER_CHALLENGE_TRACKS, + tracks: constants.FILTER_CHALLENGE_TRACKS.filter((track) => track !== "QA"), search: "", tags: [], groups: [], + events: [], startDateEnd: null, endDateStart: null, page: 1, - perPage: constants.PAGINATION_PER_PAGE[0], - sortBy: constants.CHALLENGE_SORT_BY["Most recent"], - sortOrder: null, + perPage: constants.PAGINATION_PER_PAGES[0], + sortBy: constants.CHALLENGE_SORT_BY_RECOMMENDED, + totalPrizesFrom: 0, + totalPrizesTo: 10000, + includeAllTags: true, // --- bucket: constants.FILTER_BUCKETS[1], - prizeFrom: 0, - prizeTo: 10000, - subCommunities: [], + recommended: true, }, }; +function onInitApp(state, { payload }) { + return { + ...state, + challenge: { ...state.challenge, ...payload }, + }; +} + function onUpdateFilter(state, { payload }) { + return { + ...state, + challenge: { ...state.challenge, ...payload }, + }; +} + +function onClearChallengeFilter(state, { payload }) { return { ...state, challenge: { ...state.challenge, ...payload } }; } export default handleActions( { + INIT_APP: onInitApp, UPDATE_FILTER: onUpdateFilter, + CLEAR_CHALLENGE_FILTER: onClearChallengeFilter, }, defaultState ); + +export const initialChallengeFilter = _.cloneDeep(defaultState.challenge); diff --git a/src/root.component.js b/src/root.component.js index a49494c..a4f064f 100644 --- a/src/root.component.js +++ b/src/root.component.js @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import React, { useEffect } from "react"; import { setAppMenu } from "@topcoder/micro-frontends-navbar-app"; import appMenu from "./constants/appMenu"; @@ -18,7 +19,10 @@ export default function Root() { return ( - + <> + + {process.env.NODE_ENV === "test" && } + ); diff --git a/src/root.component.test.js b/src/root.component.test.js index 55a471d..af3e061 100644 --- a/src/root.component.test.js +++ b/src/root.component.test.js @@ -5,6 +5,6 @@ import Root from "./root.component"; describe("Root component", () => { it("should be in the document", () => { const { getByText } = render(); - expect(getByText(/React child app example/i)).toBeInTheDocument(); + expect(getByText(/Earn App/)).toBeInTheDocument(); }); }); diff --git a/src/services/api.js b/src/services/api.js index 85af26c..1c2d73f 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -37,7 +37,7 @@ async function get(endpoint) { } async function post(endpoint, body) { - const response = doFetch(endpoint, { + const response = await doFetch(endpoint, { body, method: "POST", }); @@ -45,7 +45,7 @@ async function post(endpoint, body) { } async function put(endpoint, body) { - const response = doFetch(endpoint, { + const response = await doFetch(endpoint, { body, method: "PUT", }); @@ -53,7 +53,7 @@ async function put(endpoint, body) { } async function patch(endpoint, body) { - const response = doFetch(endpoint, { + const response = await doFetch(endpoint, { body, method: "PATCH", }); diff --git a/src/services/lookup.js b/src/services/lookup.js index 17b45b0..ce1478c 100644 --- a/src/services/lookup.js +++ b/src/services/lookup.js @@ -1,6 +1,6 @@ import api from "./api"; import qs from "qs"; -import * as util from "../utils/auth"; +import * as utils from "../utils"; async function getTags() { const v3 = true; @@ -19,22 +19,33 @@ async function getTags() { } async function doGetUserGroups() { - return api.get( - `/groups?memberId=${await util.getUserId()}&membershipType=user` - ); + const isLoggedIn = await utils.auth.isLoggedIn(); + + if (isLoggedIn) { + const userId = await utils.auth.getUserId(); + return api.get(`/groups?memberId=${userId}&membershipType=user`); + } + + return []; } async function getCommunityList() { const groups = await doGetUserGroups(); - const communityListQuery = qs.stringify({ groups: groups.map((g) => g.id) }); + let communityListQuery = qs.stringify({ groups: groups.map((g) => g.id) }); + communityListQuery = communityListQuery + ? `?${communityListQuery}` + : communityListQuery; + const response = await api.doFetch( - `/community-app-assets/api/tc-communities/?${communityListQuery}`, + `/community-app-assets/api/tc-communities${communityListQuery}`, {}, null, process.env.URL.COMMUNITY_APP // eslint-disable-line no-undef ); - const data = await response.json(); - return data.list; + let communities = await response.json(); + return communities.filter( + (community) => !utils.challenge.isHiddenCommunity(community) + ); } export default { diff --git a/src/styles/GUIKit/Includes/_mixin-utils.scss b/src/styles/GUIKit/Includes/_mixin-utils.scss new file mode 100644 index 0000000..cf96c7b --- /dev/null +++ b/src/styles/GUIKit/Includes/_mixin-utils.scss @@ -0,0 +1,30 @@ +@mixin dropdown-hide-chevron { + :global { + .dropdownContainer { + .Select-arrow-zone, + .Select + img { + display: none !important; + } + } + } +} + +@mixin dropdown-show-label { + :global { + .dropdownContainer { + .dropdownLabel { + display: flex !important; + } + } + } +} + +@mixin textinput-show-label { + :global { + .textInputContainer { + .textInputLabel { + display: flex !important; + } + } + } +} diff --git a/src/styles/GUIKit/Includes/index.scss b/src/styles/GUIKit/Includes/index.scss index 7794723..ccd44a6 100644 --- a/src/styles/GUIKit/Includes/index.scss +++ b/src/styles/GUIKit/Includes/index.scss @@ -1 +1,2 @@ @import 'mixin'; +@import 'mixin-utils'; diff --git a/src/styles/_fonts.scss b/src/styles/_fonts.scss index 486ce20..1594c00 100644 --- a/src/styles/_fonts.scss +++ b/src/styles/_fonts.scss @@ -48,60 +48,7 @@ // 800 Extra Bold (Ultra Bold) // 900 Black (Heavy) -// Roboto -@include font-family('Roboto', 900, normal, '../assets/fonts/roboto/roboto-black'); -@include font-family('Roboto', 900, italic, '../assets/fonts/roboto/roboto-blackitalic'); -@include font-family('Roboto', 700, normal, '../assets/fonts/roboto/roboto-bold'); -@include font-family('Roboto', 700, italic, '../assets/fonts/roboto/roboto-bolditalic'); -@include font-family('Roboto', 500, normal, '../assets/fonts/roboto/roboto-medium'); -@include font-family('Roboto', 500, italic, '../assets/fonts/roboto/roboto-mediumitalic'); -@include font-family('Roboto', 400, normal, '../assets/fonts/roboto/roboto-regular'); -@include font-family('Roboto', 400, italic, '../assets/fonts/roboto/roboto-italic'); -@include font-family('Roboto', 300, normal, '../assets/fonts/roboto/roboto-light'); -@include font-family('Roboto', 300, italic, '../assets/fonts/roboto/roboto-lightitalic'); -@include font-family('Roboto', 100, normal, '../assets/fonts/roboto/roboto-thin'); -@include font-family('Roboto', 100, italic, '../assets/fonts/roboto/roboto-thinitalic'); +/// Roboto +/// Barlow Condensed +/// Barlow -// Barlow Condensed -@include font-family-ttf('BarlowCondensed', 900, normal, '../assets/fonts/barlow-condensed/barlow-condensed-black'); -@include font-family-ttf('BarlowCondensed', 900, italic, '../assets/fonts/barlow-condensed/barlow-condensed-blackitalic'); - -@include font-family-ttf('BarlowCondensed', 700, normal, '../assets/fonts/barlow-condensed/barlow-condensed-bold'); -@include font-family-ttf('BarlowCondensed', 700, italic, '../assets/fonts/barlow-condensed/barlow-condensed-bolditalic'); - -@include font-family-ttf('BarlowCondensed', 600, normal, '../assets/fonts/barlow-condensed/barlow-condensed-semibold'); -@include font-family-ttf('BarlowCondensed', 600, italic, '../assets/fonts/barlow-condensed/barlow-condensed-semibolditalic'); - -@include font-family-ttf('BarlowCondensed', 500, normal, '../assets/fonts/barlow-condensed/barlow-condensed-medium'); -@include font-family-ttf('BarlowCondensed', 500, italic, '../assets/fonts/barlow-condensed/barlow-condensed-mediumitalic'); - -@include font-family-ttf('BarlowCondensed', 400, normal, '../assets/fonts/barlow-condensed/barlow-condensed-regular'); -@include font-family-ttf('BarlowCondensed', 400, italic, '../assets/fonts/barlow-condensed/barlow-condensed-italic'); - -@include font-family-ttf('BarlowCondensed', 300, normal, '../assets/fonts/barlow-condensed/barlow-condensed-light'); -@include font-family-ttf('BarlowCondensed', 300, italic, '../assets/fonts/barlow-condensed/barlow-condensed-lightitalic'); - -@include font-family-ttf('BarlowCondensed', 100, normal, '../assets/fonts/barlow-condensed/barlow-condensed-thin'); -@include font-family-ttf('BarlowCondensed', 100, italic, '../assets/fonts/barlow-condensed/barlow-condensed-thinitalic'); - -// Barlow -@include font-family-ttf('Barlow', 900, normal, '../assets/fonts/barlow/barlow-black'); -@include font-family-ttf('Barlow', 900, italic, '../assets/fonts/barlow/barlow-blackitalic'); - -@include font-family-ttf('Barlow', 700, normal, '../assets/fonts/barlow/barlow-bold'); -@include font-family-ttf('Barlow', 700, italic, '../assets/fonts/barlow/barlow-bolditalic'); - -@include font-family-ttf('Barlow', 600, normal, '../assets/fonts/barlow/barlow-semibold'); -@include font-family-ttf('Barlow', 600, italic, '../assets/fonts/barlow/barlow-semibolditalic'); - -@include font-family-ttf('Barlow', 500, normal, '../assets/fonts/barlow/barlow-medium'); -@include font-family-ttf('Barlow', 500, italic, '../assets/fonts/barlow/barlow-mediumitalic'); - -@include font-family-ttf('Barlow', 400, normal, '../assets/fonts/barlow/barlow-regular'); -@include font-family-ttf('Barlow', 400, italic, '../assets/fonts/barlow/barlow-italic'); - -@include font-family-ttf('Barlow', 300, normal, '../assets/fonts/barlow/barlow-light'); -@include font-family-ttf('Barlow', 300, italic, '../assets/fonts/barlow/barlow-lightitalic'); - -@include font-family-ttf('Barlow', 100, normal, '../assets/fonts/barlow/barlow-thin'); -@include font-family-ttf('Barlow', 100, italic, '../assets/fonts/barlow/barlow-thinitalic'); diff --git a/src/styles/_mixins.scss b/src/styles/_mixins.scss index dabdae2..8e81aa9 100644 --- a/src/styles/_mixins.scss +++ b/src/styles/_mixins.scss @@ -1,5 +1,6 @@ @import "mixins/typography"; @import "mixins/media"; +@import "GUIKit/Includes"; // Placeholder @mixin placeholder { diff --git a/src/styles/_utils.scss b/src/styles/_utils.scss index f6b65bc..d6bf2a2 100644 --- a/src/styles/_utils.scss +++ b/src/styles/_utils.scss @@ -4,6 +4,7 @@ .sidebar { flex: 0 0 270px; + min-height: calc(100vh - 60px); background: $white; > hr { diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss index 433ec8b..82bc7ae 100644 --- a/src/styles/_variables.scss +++ b/src/styles/_variables.scss @@ -141,3 +141,5 @@ $border-radius-lg: 8px; $page-padding-x: 7 * $base-unit; $page-padding-y: 32px; + +$screen-xxl: 1366px; \ No newline at end of file diff --git a/src/styles/mixins/_media.scss b/src/styles/mixins/_media.scss index e6ec7cf..028c8dc 100644 --- a/src/styles/mixins/_media.scss +++ b/src/styles/mixins/_media.scss @@ -22,6 +22,24 @@ $screen-lg: 1280px !default; } } +@mixin xs-to-sm { + @media (max-width: #{$screen-sm}) { + @content; + } +} + +@mixin xs-to-md { + @media (max-width: #{$screen-md}) { + @content; + } +} + +@mixin xs-to-lg { + @media (max-width: #{$screen-lg}) { + @content; + } +} + /* XS */ @mixin sm { @media (min-width: #{$screen-xs + 1px}) and (max-width: #{$screen-sm}) { @@ -29,6 +47,18 @@ $screen-lg: 1280px !default; } } +@mixin sm-to-md { + @media (min-width: #{$screen-xs + 1px}) and (max-width: #{$screen-md}) { + @content; + } +} + +@mixin sm-to-xl { + @media (min-width: #{$screen-xs + 1px}) { + @content; + } +} + /* MD */ @mixin md { @media (min-width: #{$screen-sm + 1px}) and (max-width: #{$screen-md}) { @@ -36,6 +66,18 @@ $screen-lg: 1280px !default; } } +@mixin md-to-lg { + @media (min-width: #{$screen-sm + 1px}) and (max-width: #{$screen-lg}) { + @content; + } +} + +@mixin md-to-xl { + @media (min-width: #{$screen-sm + 1px}) { + @content; + } +} + /* LG */ @mixin lg { @media (min-width: #{$screen-md + 1px}) and (max-width: #{$screen-lg}) { @@ -43,6 +85,12 @@ $screen-lg: 1280px !default; } } +@mixin lg-to-xl { + @media (min-width: #{$screen-md + 1px}) { + @content; + } +} + /* XL */ @mixin xl { @media (min-width: #{$screen-lg + 1px}) { diff --git a/src/styles/mixins/_typography.scss b/src/styles/mixins/_typography.scss index df6ae0f..376a340 100644 --- a/src/styles/mixins/_typography.scss +++ b/src/styles/mixins/_typography.scss @@ -39,37 +39,37 @@ // Barlow Condensed @mixin barlow-condensed-black { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 900; } @mixin barlow-condensed-bold { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 700; } @mixin barlow-condensed-semibold { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 600; } @mixin barlow-condensed-medium { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 500; } @mixin barlow-condensed-regular { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 400; } @mixin barlow-condensed-light { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 300; } @mixin barlow-condensed-thin { - font-family: "BarlowCondensed", Helvetica, Arial, sans-serif; + font-family: "Barlow Condensed", Helvetica, Arial, sans-serif; font-weight: 100; } diff --git a/src/styles/react-date-range/default.scss b/src/styles/react-date-range/default.scss deleted file mode 100644 index 414e480..0000000 --- a/src/styles/react-date-range/default.scss +++ /dev/null @@ -1,400 +0,0 @@ -:global { - -.rdrCalendarWrapper{ - color: #000000; - font-size: 12px; -} - -.rdrDateDisplayWrapper{ - background-color: rgb(239, 242, 247); -} - -.rdrDateDisplay{ - margin: 0.833em; -} - -.rdrDateDisplayItem{ - border-radius: 4px; - background-color: rgb(255, 255, 255); - box-shadow: 0 1px 2px 0 rgba(35, 57, 66, 0.21); - border: 1px solid transparent; -} - -.rdrDateDisplayItem input{ - cursor: pointer; - height: 2.5em; - line-height: 2.5em; - border: 0px; - background: transparent; - width: 100%; - color: #849095; - } - -.rdrDateDisplayItemActive{ - border-color: currentColor; -} - -.rdrDateDisplayItemActive input{ - color: #7d888d - } - -.rdrMonthAndYearWrapper { - -webkit-box-align: center; - align-items: center; - height: 60px; - padding-top: 10px; -} - -.rdrMonthAndYearPickers{ - font-weight: 600; -} - -.rdrMonthAndYearPickers select{ - -moz-appearance: none; - appearance: none; - -webkit-appearance: none; - border: 0; - background: transparent; - padding: 10px 30px 10px 10px; - border-radius: 4px; - outline: 0; - color: #3e484f; - background: url("data:image/svg+xml;utf8,") no-repeat; - background-position: right 8px center; - cursor: pointer; - text-align: center - } - -.rdrMonthAndYearPickers select:hover{ - background-color: rgba(0,0,0,0.07); - } - -.rdrMonthPicker, .rdrYearPicker{ - margin: 0 5px -} - -.rdrNextPrevButton { - display: block; - width: 24px; - height: 24px; - margin: 0 0.833em; - padding: 0; - border: 0; - border-radius: 5px; - background: #EFF2F7 -} - -.rdrNextPrevButton:hover{ - background: #E1E7F0; - } - -.rdrNextPrevButton i { - display: block; - width: 0; - height: 0; - padding: 0; - text-align: center; - border-style: solid; - margin: auto; - -webkit-transform: translate(-3px, 0px); - transform: translate(-3px, 0px); - } - -.rdrPprevButton i { - border-width: 4px 6px 4px 4px; - border-color: transparent rgb(52, 73, 94) transparent transparent; - -webkit-transform: translate(-3px, 0px); - transform: translate(-3px, 0px); - } - -.rdrNextButton i { - margin: 0 0 0 7px; - border-width: 4px 4px 4px 6px; - border-color: transparent transparent transparent rgb(52, 73, 94); - -webkit-transform: translate(3px, 0px); - transform: translate(3px, 0px); - } - -.rdrWeekDays { - padding: 0 0.833em; -} - -.rdrMonth{ - padding: 0 0.833em 1.666em 0.833em; -} - -.rdrMonth .rdrWeekDays { - padding: 0; - } - -.rdrMonths.rdrMonthsVertical .rdrMonth:first-child .rdrMonthName{ - display: none; -} - -.rdrWeekDay { - font-weight: 400; - line-height: 2.667em; - color: rgb(132, 144, 149); -} - -.rdrDay { - background: transparent; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - border: 0; - padding: 0; - line-height: 3.000em; - height: 3.000em; - text-align: center; - color: #1d2429 -} - -.rdrDay:focus { - outline: 0; - } - -.rdrDayNumber { - outline: 0; - font-weight: 300; - position: absolute; - left: 0; - right: 0; - top: 0; - bottom: 0; - top: 5px; - bottom: 5px; - display: -webkit-box; - display: flex; - -webkit-box-align: center; - align-items: center; - -webkit-box-pack: center; - justify-content: center; -} - -.rdrDayToday .rdrDayNumber span{ - font-weight: 500 -} - -.rdrDayToday .rdrDayNumber span:after{ - content: ''; - position: absolute; - bottom: 4px; - left: 50%; - -webkit-transform: translate(-50%, 0); - transform: translate(-50%, 0); - width: 18px; - height: 2px; - border-radius: 2px; - background: #3d91ff; - } - -.rdrDayToday:not(.rdrDayPassive) .rdrInRange ~ .rdrDayNumber span:after,.rdrDayToday:not(.rdrDayPassive) .rdrStartEdge ~ .rdrDayNumber span:after,.rdrDayToday:not(.rdrDayPassive) .rdrEndEdge ~ .rdrDayNumber span:after,.rdrDayToday:not(.rdrDayPassive) .rdrSelected ~ .rdrDayNumber span:after{ - background: #fff; - } - -.rdrDay:not(.rdrDayPassive) .rdrInRange ~ .rdrDayNumber span,.rdrDay:not(.rdrDayPassive) .rdrStartEdge ~ .rdrDayNumber span,.rdrDay:not(.rdrDayPassive) .rdrEndEdge ~ .rdrDayNumber span,.rdrDay:not(.rdrDayPassive) .rdrSelected ~ .rdrDayNumber span{ - color: rgba(255, 255, 255, 0.85); - } - -.rdrSelected, .rdrInRange, .rdrStartEdge, .rdrEndEdge{ - background: currentColor; - position: absolute; - top: 5px; - left: 0; - right: 0; - bottom: 5px; -} - -.rdrSelected{ - left: 2px; - right: 2px; -} - -.rdrInRange{} - -.rdrStartEdge{ - border-top-left-radius: 1.042em; - border-bottom-left-radius: 1.042em; - left: 2px; -} - -.rdrEndEdge{ - border-top-right-radius: 1.042em; - border-bottom-right-radius: 1.042em; - right: 2px; -} - -.rdrSelected{ - border-radius: 1.042em; -} - -.rdrDayStartOfMonth .rdrInRange, .rdrDayStartOfMonth .rdrEndEdge, .rdrDayStartOfWeek .rdrInRange, .rdrDayStartOfWeek .rdrEndEdge{ - border-top-left-radius: 1.042em; - border-bottom-left-radius: 1.042em; - left: 2px; - } - -.rdrDayEndOfMonth .rdrInRange, .rdrDayEndOfMonth .rdrStartEdge, .rdrDayEndOfWeek .rdrInRange, .rdrDayEndOfWeek .rdrStartEdge{ - border-top-right-radius: 1.042em; - border-bottom-right-radius: 1.042em; - right: 2px; - } - -.rdrDayStartOfMonth .rdrDayInPreview, .rdrDayStartOfMonth .rdrDayEndPreview, .rdrDayStartOfWeek .rdrDayInPreview, .rdrDayStartOfWeek .rdrDayEndPreview{ - border-top-left-radius: 1.333em; - border-bottom-left-radius: 1.333em; - border-left-width: 1px; - left: 0px; - } - -.rdrDayEndOfMonth .rdrDayInPreview, .rdrDayEndOfMonth .rdrDayStartPreview, .rdrDayEndOfWeek .rdrDayInPreview, .rdrDayEndOfWeek .rdrDayStartPreview{ - border-top-right-radius: 1.333em; - border-bottom-right-radius: 1.333em; - border-right-width: 1px; - right: 0px; - } - -.rdrDayStartPreview, .rdrDayInPreview, .rdrDayEndPreview{ - background: rgba(255, 255, 255, 0.09); - position: absolute; - top: 3px; - left: 0px; - right: 0px; - bottom: 3px; - pointer-events: none; - border: 0px solid currentColor; - z-index: 1; -} - -.rdrDayStartPreview{ - border-top-width: 1px; - border-left-width: 1px; - border-bottom-width: 1px; - border-top-left-radius: 1.333em; - border-bottom-left-radius: 1.333em; - left: 0px; -} - -.rdrDayInPreview{ - border-top-width: 1px; - border-bottom-width: 1px; -} - -.rdrDayEndPreview{ - border-top-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - border-top-right-radius: 1.333em; - border-bottom-right-radius: 1.333em; - right: 2px; - right: 0px; -} - -.rdrDefinedRangesWrapper{ - font-size: 12px; - width: 226px; - border-right: solid 1px #eff2f7; - background: #fff; -} - -.rdrDefinedRangesWrapper .rdrStaticRangeSelected{ - color: currentColor; - font-weight: 600; - } - -.rdrStaticRange{ - border: 0; - cursor: pointer; - display: block; - outline: 0; - border-bottom: 1px solid #eff2f7; - padding: 0; - background: #fff -} - -.rdrStaticRange:hover .rdrStaticRangeLabel,.rdrStaticRange:focus .rdrStaticRangeLabel{ - background: #eff2f7; - } - -.rdrStaticRangeLabel{ - display: block; - outline: 0; - line-height: 18px; - padding: 10px 20px; - text-align: left; -} - -.rdrInputRanges{ - padding: 10px 0; -} - -.rdrInputRange{ - -webkit-box-align: center; - align-items: center; - padding: 5px 20px; -} - -.rdrInputRangeInput{ - width: 30px; - height: 30px; - line-height: 30px; - border-radius: 4px; - text-align: center; - border: solid 1px rgb(222, 231, 235); - margin-right: 10px; - color: rgb(108, 118, 122) -} - -.rdrInputRangeInput:focus, .rdrInputRangeInput:hover{ - border-color: rgb(180, 191, 196); - outline: 0; - color: #333; - } - -.rdrCalendarWrapper:not(.rdrDateRangeWrapper) .rdrDayHovered .rdrDayNumber:after{ - content: ''; - border: 1px solid currentColor; - border-radius: 1.333em; - position: absolute; - top: -2px; - bottom: -2px; - left: 0px; - right: 0px; - background: transparent; -} - -.rdrDayPassive{ - pointer-events: none; -} - -.rdrDayPassive .rdrDayNumber span{ - color: #d5dce0; - } - -.rdrDayPassive .rdrInRange, .rdrDayPassive .rdrStartEdge, .rdrDayPassive .rdrEndEdge, .rdrDayPassive .rdrSelected, .rdrDayPassive .rdrDayStartPreview, .rdrDayPassive .rdrDayInPreview, .rdrDayPassive .rdrDayEndPreview{ - display: none; - } - -.rdrDayDisabled { - background-color: rgb(248, 248, 248); -} - -.rdrDayDisabled .rdrDayNumber span{ - color: #aeb9bf; - } - -.rdrDayDisabled .rdrInRange, .rdrDayDisabled .rdrStartEdge, .rdrDayDisabled .rdrEndEdge, .rdrDayDisabled .rdrSelected, .rdrDayDisabled .rdrDayStartPreview, .rdrDayDisabled .rdrDayInPreview, .rdrDayDisabled .rdrDayEndPreview{ - -webkit-filter: grayscale(100%) opacity(60%); - filter: grayscale(100%) opacity(60%); - } - -.rdrMonthName{ - text-align: left; - font-weight: 600; - color: #849095; - padding: 0.833em; -} - -} // end :global diff --git a/src/styles/react-date-range/styles.scss b/src/styles/react-date-range/styles.scss deleted file mode 100644 index 952df72..0000000 --- a/src/styles/react-date-range/styles.scss +++ /dev/null @@ -1,226 +0,0 @@ -:global { - -.rdrCalendarWrapper { - box-sizing: border-box; - background: #ffffff; - display: -webkit-inline-box; - display: inline-flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.rdrDateDisplay{ - display: -webkit-box; - display: flex; - -webkit-box-pack: justify; - justify-content: space-between; -} - -.rdrDateDisplayItem{ - -webkit-box-flex: 1; - flex: 1 1; - width: 0; - text-align: center; - color: inherit; -} - -.rdrDateDisplayItem + .rdrDateDisplayItem{ - margin-left: 0.833em; - } - -.rdrDateDisplayItem input{ - text-align: inherit - } - -.rdrDateDisplayItem input:disabled{ - cursor: default; - } - -.rdrDateDisplayItemActive{} - -.rdrMonthAndYearWrapper { - box-sizing: inherit; - display: -webkit-box; - display: flex; - -webkit-box-pack: justify; - justify-content: space-between; -} - -.rdrMonthAndYearPickers{ - -webkit-box-flex: 1; - flex: 1 1 auto; - display: -webkit-box; - display: flex; - -webkit-box-pack: center; - justify-content: center; - -webkit-box-align: center; - align-items: center; -} - -.rdrMonthPicker{} - -.rdrYearPicker{} - -.rdrNextPrevButton { - box-sizing: inherit; - cursor: pointer; - outline: none; -} - -.rdrPprevButton {} - -.rdrNextButton {} - -.rdrMonths{ - display: -webkit-box; - display: flex; -} - -.rdrMonthsVertical{ - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; -} - -.rdrMonthsHorizontal > div > div > div{ - display: -webkit-box; - display: flex; - -webkit-box-orient: horizontal; - -webkit-box-direction: normal; - flex-direction: row; -} - -.rdrMonth{ - width: 27.667em; -} - -.rdrWeekDays{ - display: -webkit-box; - display: flex; -} - -.rdrWeekDay { - flex-basis: calc(100% / 7); - box-sizing: inherit; - text-align: center; -} - -.rdrDays{ - display: -webkit-box; - display: flex; - flex-wrap: wrap; -} - -.rdrDateDisplayWrapper{} - -.rdrMonthName{} - -.rdrInfiniteMonths{ - overflow: auto; -} - -.rdrDateRangeWrapper{ - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.rdrDateInput { - position: relative; -} - -.rdrDateInput input { - outline: none; - } - -.rdrDateInput .rdrWarning { - position: absolute; - font-size: 1.6em; - line-height: 1.6em; - top: 0; - right: .25em; - color: #FF0000; - } - -.rdrDay { - box-sizing: inherit; - width: calc(100% / 7); - position: relative; - font: inherit; - cursor: pointer; -} - -.rdrDayNumber { - display: block; - position: relative; -} - -.rdrDayNumber span{ - color: #1d2429; - } - -.rdrDayDisabled { - cursor: not-allowed; -} - -@supports (-ms-ime-align: auto) { - .rdrDay { - flex-basis: 14.285% !important; - } -} - -.rdrSelected, .rdrInRange, .rdrStartEdge, .rdrEndEdge{ - pointer-events: none; -} - -.rdrInRange{} - -.rdrDayStartPreview, .rdrDayInPreview, .rdrDayEndPreview{ - pointer-events: none; -} - -.rdrDayHovered{} - -.rdrDayActive{} - -.rdrDateRangePickerWrapper{ - display: -webkit-inline-box; - display: inline-flex; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.rdrDefinedRangesWrapper{} - -.rdrStaticRanges{ - display: -webkit-box; - display: flex; - -webkit-box-orient: vertical; - -webkit-box-direction: normal; - flex-direction: column; -} - -.rdrStaticRange{ - font-size: inherit; -} - -.rdrStaticRangeLabel{} - -.rdrInputRanges{} - -.rdrInputRange{ - display: -webkit-box; - display: flex; -} - -.rdrInputRangeInput{} - -} // end :global diff --git a/src/topcoder-micro-frontends-earn-app.js b/src/topcoder-micro-frontends-earn-app.js index 8ad1e10..6b1ae2d 100644 --- a/src/topcoder-micro-frontends-earn-app.js +++ b/src/topcoder-micro-frontends-earn-app.js @@ -3,6 +3,9 @@ import React from "react"; import ReactDOM from "react-dom"; import singleSpaReact from "single-spa-react"; import Root from "./root.component"; +import appInit from "./utils/lifeCycle"; + +const appLifecycles = appInit(); const lifecycles = singleSpaReact({ React, @@ -14,9 +17,9 @@ const lifecycles = singleSpaReact({ }, }); -const bootstrap = [lifecycles.bootstrap]; +const bootstrap = [appLifecycles.bootstrap, lifecycles.bootstrap]; -const mount = [lifecycles.mount]; +const mount = [appLifecycles.mount, lifecycles.mount]; const unmount = [lifecycles.unmount]; diff --git a/src/utils/auth.js b/src/utils/auth.js index 6ae0019..31fdfd5 100644 --- a/src/utils/auth.js +++ b/src/utils/auth.js @@ -1,7 +1,15 @@ import _ from "lodash"; -import { getAuthUserProfile } from "@topcoder/micro-frontends-navbar-app"; +import { + getAuthUserProfile, + getAuthUserTokens, +} from "@topcoder/micro-frontends-navbar-app"; export async function getUserId() { const profile = await getAuthUserProfile(); return profile.userId; } + +export async function isLoggedIn() { + const { tokenV3, tokenV2 } = await getAuthUserTokens(); + return tokenV3 != null || tokenV2 != null; +} diff --git a/src/utils/challenge.js b/src/utils/challenge.js index 158f9ea..e6f078f 100644 --- a/src/utils/challenge.js +++ b/src/utils/challenge.js @@ -2,21 +2,170 @@ import moment from "moment"; import "moment-duration-format"; import _ from "lodash"; import * as constants from "../constants"; +import Joi from "joi"; +import { initialChallengeFilter } from "../reducers/filter"; + +Joi.optionalId = () => Joi.string().uuid(); +Joi.page = () => Joi.number().integer().min(1); +Joi.perPage = () => + Joi.number() + .integer() + .min(1) + .max(100) + .valid(...constants.PAGINATION_PER_PAGES); +Joi.bucket = () => + Joi.string().custom((param) => + constants.FILTER_BUCKETS.find( + (bucket) => param && param.toLowerCase() === bucket.toLowerCase() + ) + ); +Joi.track = () => + Joi.string().custom((param) => + _.findKey( + constants.FILTER_CHALLENGE_TRACK_ABBREVIATIONS, + (trackAbbreviation) => + param && param.toLowerCase() === trackAbbreviation.toLowerCase() + ) + ); +Joi.type = () => + Joi.string().custom((param) => + _.findKey( + constants.FILTER_CHALLENGE_TYPE_ABBREVIATIONS, + (typeAbbreviation) => + param && param.toLowerCase() === typeAbbreviation.toLowerCase() + ) + ); + +export function getCurrencySymbol(prizeSets) { + return constants.CURRENCY_SYMBOL[_.get(prizeSets, "[0].prizes[0].type", "")]; +} + +export function getPlacementPrizes(prizeSets) { + const placementSet = + prizeSets && prizeSets.find((prizeSet) => prizeSet.type === "placement"); + return placementSet && placementSet.prizes + ? placementSet.prizes.map((p) => p.value) + : []; +} + +export function getCheckpointPrizes(prizeSets) { + const checkpointSet = + prizeSets && prizeSets.find((prizeSet) => prizeSet.type === "checkpoint"); + return checkpointSet && checkpointSet.prizes + ? checkpointSet.prizes.map((p) => p.value) + : []; +} /** - * Return the sum of placements, or `undefined` if not found. - * @return {number|undefined} + * Convert the query of `/earn/find/challenges` path into the challenge filter. + * + * @param {Object} params The query params + * + * @return {Object} normalized challenge filter */ -export function getChallengePurse(prizeSets) { - const placementSet = prizeSets.find( - (prizeSet) => prizeSet.type === "placement" +export function createChallengeFilter(params) { + const schema = createChallengeFilter.schema; + const normalized = Joi.attempt( + params, + schema, + { abortEarly: false, stripUnknown: true }, + (err, value) => { + let invalidAttributes = []; + if (err) { + invalidAttributes = err.details.reduce( + (arr, detail) => arr.concat(detail.path.join(".")), + [] + ); + } + + return _.omit(value, invalidAttributes); + } ); - const prizes = placementSet && placementSet.prizes ? placementSet.prizes : []; - return ( - prizes.length && prizes.reduce((sum, placement) => sum + placement.value, 0) + + return _.omitBy( + { + page: normalized.page, + perPage: normalized.perPage, + types: normalized.types, + tracks: normalized.tracks, + search: normalized.search, + tags: normalized.tags, + startDateEnd: + normalized.startDateEnd && normalized.startDateEnd.toISOString(), + endDateStart: + normalized.endDateStart && normalized.endDateStart.toISOString(), + sortBy: normalized.sortBy, + groups: normalized.groups, + events: normalized.events, + bucket: normalized.bucket, + totalPrizesFrom: normalized.totalPrizesFrom, + totalPrizesTo: normalized.totalPrizesTo, + includeAllTags: normalized.includeAllTags, + recommended: normalized.recommended, + }, + (value) => value == null ); } +const queryScheme = { + page: Joi.page(), + perPage: Joi.perPage(), + types: Joi.array().items(Joi.type()), + tracks: Joi.array().items(Joi.track()), + search: Joi.string(), + tags: Joi.array().items(Joi.string()), + startDateEnd: Joi.date(), + endDateStart: Joi.date(), + sortBy: Joi.string().valid( + "bestMatch", + "updated", + "overview.totalPrizes", + "name" + ), + groups: Joi.array().items(Joi.optionalId()).unique(), + events: Joi.array().items(Joi.string()), + bucket: Joi.bucket(), + totalPrizesFrom: Joi.number().integer().min(0), + totalPrizesTo: Joi.number().integer().min(0), + includeAllTags: Joi.boolean(), + recommended: Joi.boolean(), +}; + +/** + * Define the validation scheme for `/earn/find/challenges` url query + * + * The scheme is used when the app is mounted with `/earn/find/challenges` request. + */ +createChallengeFilter.schema = Joi.object().keys(queryScheme).unknown(true); + +/** + * Return the query params corresponding to the filter + * + * @param {Object} filter The challenge filter + * + * @return {Object} query params + */ +export function createChallengeParams(filter) { + let params = _.pick(filter, Object.keys(queryScheme)); + return { + ...params, + types: params.types.map( + (type) => constants.FILTER_CHALLENGE_TYPE_ABBREVIATIONS[type] + ), + tracks: params.tracks.map( + (track) => constants.FILTER_CHALLENGE_TRACK_ABBREVIATIONS[track] + ), + includeAllTags: params.tags.length ? params.includeAllTags : null, + }; +} + +/** + * Convert the frontend filter into the backend criteria. + * + * @param {Object} filter The frontend filter + * + * @return {Object} challenge criteria + */ export function createChallengeCriteria(filter) { return { page: filter.page, @@ -34,33 +183,66 @@ export function createChallengeCriteria(filter) { startDateEnd: filter.startDateEnd, endDateStart: filter.endDateStart, endDateEnd: filter.endDateEnd, - sortBy: filter.sortBy, - sortOrder: filter.sortOrder, + sortBy: validateSortBy(filter.sortBy), + sortOrder: constants.SORT_BY_SORT_ORDER[filter.sortBy], groups: filter.groups, + events: filter.events, + totalPrizesFrom: filter.totalPrizesFrom, + totalPrizesTo: filter.totalPrizesTo, + includeAllTags: filter.tags.length ? filter.includeAllTags : null, + isLightweight: true, }; } +/** + * Return the current sort-by if supported; otherwise return the failback. + */ +export function validateSortBy(sortBy) { + return ["updated", "overview.totalPrizes", "name"].includes(sortBy) + ? sortBy + : constants.CHALLENGE_SORT_BY_MOST_RECENT; +} + export function createOpenForRegistrationChallengeCriteria() { return { status: "Active", currentPhaseName: "Registration", + endDateStart: null, + startDateEnd: null, }; } -export function createActiveChallengeCriteria() { +export function createAllActiveChallengeCriteria() { return { status: "Active", - currentPhaseName: "Submission", - registrationEndDateEnd: new Date().toISOString(), + endDateStart: null, + startDateEnd: null, }; } -export function createPastChallengeCriteria() { +export function createClosedChallengeCriteria() { return { status: "Completed", + sortBy: constants.CHALLENGE_SORT_BY_MOST_RECENT, + sortOrder: constants.SORT_ORDER.DESC, }; } +export function createOpenForRegistrationCountCriteria() { + return { + status: "Active", + currentPhaseName: "Registration", + isLightweight: true, + page: 1, + perPage: 1, + endDateStart: null, + startDateEnd: null, + }; +} + +/** + * Determine which frontend attributes will be capable of triggering api calls. + */ export function shouldFetchChallenges(filterUpdate) { const attributes = Object.keys(filterUpdate); return _.some(attributes, (attr) => @@ -77,23 +259,22 @@ export function shouldFetchChallenges(filterUpdate) { "endDateStart", "endDateEnd", "sortBy", - "sortOrder", "groups", + "events", + "bucket", + "totalPrizesFrom", + "totalPrizesTo", + "includeAllTags", ].includes(attr) ); } -export function shouldFilterChallenges(filterUpdate) { - const attributes = Object.keys(filterUpdate); - return _.some(attributes, (attr) => - ["prizeFrom", "prizeTo", "subCommunities"].includes(attr) - ); -} - export function checkRequiredFilterAttributes(filter) { let valid = true; if ( + !filter.tracks || filter.tracks.length === 0 || + !filter.types || filter.types.length === 0 || !filter.bucket ) { @@ -102,12 +283,84 @@ export function checkRequiredFilterAttributes(filter) { return valid; } +export function createEmptyResult() { + const result = []; + result.meta = { page: 0, perPage: 0, total: 0, totalPages: 0 }; + return result; +} + +export function createEmptyChallengeFilter() { + const filter = _.cloneDeep(initialChallengeFilter); + return _.pick(filter, [ + "types", + "tracks", + "search", + "tags", + "groups", + "events", + "startDateEnd", + "endDateStart", + "page", + "perPage", + "sortBy", + "totalPrizesFrom", + "totalPrizesTo", + "includeAllTags", + "recommended", + ]); +} + +/** + * Check if community is hidden. + * + * @param {Object<{ + * hidden: boolean, + * hideFilter: boolean, + * }>} community Community + */ +export function isHiddenCommunity(community) { + return community.hidden || community.hideFilter; +} + +/** + * Check if community is to TCO. + * + * @param {Object<{ + * challengeFilter: Object<{ events: string[] }>, + * }>} community Community + */ +export function isTCOEventCommunity(community) { + return ( + community.challengeFilter.events && + community.challengeFilter.events.length > 0 + ); +} + +/** + * Check if community associates with a group. + * + * @param {Object<{ + * groupIds: string[], + * }>} community Community + */ +export function isGroupCommunity(community) { + return community.groupIds && community.groupIds.length > 0; +} + +export function getCommunityEvent(community) { + return _.get(community, "challengeFilter.events[0]"); +} + +export function getCommunityGroup(community) { + return _.get(community, "groupIds[0]"); +} + /** * Returns phase's end date. * @param {Object} phase * @return {Date} */ -function getChallengePhaseEndDate(phase) { +export function getChallengePhaseEndDate(phase) { // Case 1: phase is still open. take the `scheduledEndDate` // Case 2: phase is not open but `scheduledStartDate` is a future date. // This means phase is not yet started. So take the `scheduledEndDate` @@ -126,6 +379,20 @@ function getChallengePhaseEndDate(phase) { return new Date(phase.actualEndDate); } +/** + * Returns phase's start date. + * @param {Object} phase + * @return {Date} + */ +export function getChallengePhaseStartDate(phase) { + // Case 1: Phase is not yet started. take the `scheduledStartDate` + if (phase.isOpen !== true && moment(phase.scheduledStartDate).isAfter()) { + return new Date(phase.scheduledStartDate); + } + // For all other cases, take the `actualStartDate` as phase is already started + return new Date(phase.actualStartDate); +} + /** * Returns challenge's end date. * @param {Object} challenge diff --git a/src/utils/icon.js b/src/utils/icon.js index 5cc61ff..1e00043 100644 --- a/src/utils/icon.js +++ b/src/utils/icon.js @@ -119,7 +119,7 @@ function createTCOEventIcon (color) { - + TCO @@ -136,3 +136,13 @@ function createTCOEventIcon (color) { ); } + +export function createBadgeElement(htmlElement, content) { + const badgeElement = document.createElement('span'); + + badgeElement.classList.add('count-badge'); + badgeElement.textContent = content; + htmlElement.appendChild(badgeElement); + + return badgeElement; +} diff --git a/src/utils/index.js b/src/utils/index.js index 62d01b8..086360d 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,3 +1,4 @@ +export * as auth from "./auth"; export * as challenge from "./challenge"; export * as icon from "./icon"; export * as menu from "./menu"; @@ -57,11 +58,19 @@ export function getSelectedDropdownTermsOptions(options) { } /* Set selected dropdown term options */ -export function setSelectedDropdownTermOptions(options, selectedValues) { +export function setSelectedDropdownTermOptions( + options, + selectedValues, + caseSensitive = true +) { options.forEach((o) => (o.selected = false)); selectedValues.forEach((value) => { - const option = options.find((option) => option.label === value); + const option = options.find((option) => + caseSensitive + ? option.label === value + : option.label.toLowerCase() === value.toLowerCase() + ); if (option) { option.selected = true; } @@ -112,8 +121,13 @@ export function setSelectedRadioOption(options, selectedValue) { * Format a number value into the integer text of money value. * Ex: 1800 -> $1,800 */ -export function formatMoneyValue(value) { - let val = value || 0; +export function formatMoneyValue(value, symbol) { + if (typeof value !== "number") { + return value || ""; + } + + const SYMBOL = symbol || ""; + let val = value; val = val.toLocaleString("en-US"); const i = val.indexOf("."); @@ -122,9 +136,9 @@ export function formatMoneyValue(value) { } if (val.startsWith("-")) { - val = `-$${val.slice(1)}`; + val = `-${SYMBOL}${val.slice(1)}`; } else { - val = `$${val}`; + val = `${SYMBOL}${val}`; } return val; @@ -133,3 +147,23 @@ export function formatMoneyValue(value) { export function clamp(value, min, max) { return Math.min(Math.max(value, min), max); } + +export function formatTotalPrizes(n) { + return n.toLocaleString(); +} + +export function parseTotalPrizes(s) { + let val = s; + if (val.endsWith("+")) { + val = val.slice(0, -1); + } + let valid = val.replace(/[,0-9]/g, "") === ""; + let n; + if (valid) { + n = +val.replace(/,/g, ""); + if (/,/.test(val)) { + valid = valid && n.toLocaleString("en-US") === val; + } + } + if (valid) return n; +} diff --git a/src/utils/lifeCycle.js b/src/utils/lifeCycle.js new file mode 100644 index 0000000..a676ecc --- /dev/null +++ b/src/utils/lifeCycle.js @@ -0,0 +1,31 @@ +import store from "../store"; +import action from "../actions/initApp"; +import * as utils from "../utils"; + +export default function appInit() { + let initialQuery; + let urlPath; + + function bootstrap() { + return Promise.resolve().then(() => { + initialQuery = window.location.search; + urlPath = window.location.pathname; + }); + } + + function mount() { + if (initialQuery && urlPath.startsWith("/earn/find/challenges")) { + const params = utils.url.parseUrlQuery(initialQuery); + const filter = utils.challenge.createChallengeFilter(params); + store.dispatch(action.initApp(filter)); + } + + return Promise.resolve(); + } + + function unmount() { + return Promise.resolve(); + } + + return { bootstrap, mount, unmount }; +} diff --git a/src/utils/menu.js b/src/utils/menu.js index b060439..a76a688 100644 --- a/src/utils/menu.js +++ b/src/utils/menu.js @@ -9,7 +9,7 @@ export class MenuSelection { } travel(root) { - Object.keys(root).forEach((key) => { + this.getMenuItems(root).forEach((key) => { if (_.isObject(root[key])) { root[key].expanded = false; root[key].branch = true; @@ -24,35 +24,37 @@ export class MenuSelection { }); } + getMenuItems(menu) { + return Object.keys(_.omit(menu, "expanded", "active", "branch", "leaf")); + } + select(name) { let found = false; const selectInternal = (root) => { - Object.keys(_.omit(root, "expanded", "active", "branch", "leaf")).forEach( - (key) => { - if (found) { - return; - } + this.getMenuItems(root).forEach((key) => { + if (found) { + return; + } - if (key !== name) { - if (root[key].branch) { - selectInternal(root[key]); - } else { - root[key].active = false; - } + if (key !== name) { + if (root[key].branch) { + selectInternal(root[key]); } else { - if (root[key].leaf) { - root[key].active = true; - this.selectedMenuItem = name; - } else { - root[key].expanded = !root[key].expanded; - } - - found = true; - this.emitSelectionEvent(); + root[key].active = false; } + } else { + if (root[key].leaf) { + root[key].active = true; + this.selectedMenuItem = name; + } else { + root[key].expanded = !root[key].expanded; + } + + found = true; + this.emitSelectionEvent(); } - ); + }); }; selectInternal(this.menu); @@ -62,17 +64,15 @@ export class MenuSelection { let leaf = false; const isLeafInternal = (root) => { - Object.keys(_.omit(root, "expanded", "active", "branch", "leaf")).forEach( - (key) => { - if (key !== name) { - if (root[key].branch) { - isLeafInternal(root[key]); - } - } else if (root[key].leaf) { - leaf = true; + this.getMenuItems(root).forEach((key) => { + if (key !== name) { + if (root[key].branch) { + isLeafInternal(root[key]); } + } else if (root[key].leaf) { + leaf = true; } - ); + }); }; isLeafInternal(this.menu); @@ -85,20 +85,18 @@ export class MenuSelection { } isExpanded(name) { - let expanded = false; + let expanded; const isExpandedInternal = (root) => { - Object.keys(_.omit(root, "expanded", "active", "branch", "leaf")).forEach( - (key) => { - if (key !== name) { - if (root[key].branch) { - isExpandedInternal(root[key]); - } - } else if (root[key].branch) { - expanded = root[key].expanded; + this.getMenuItems(root).forEach((key) => { + if (key !== name) { + if (root[key].branch) { + isExpandedInternal(root[key]); } + } else if (root[key].branch) { + expanded = root[key].expanded; } - ); + }); }; isExpandedInternal(this.menu); @@ -123,20 +121,18 @@ export class MenuSelection { } const isActiveInternal = (root) => { - Object.keys(_.omit(root, "expanded", "active", "branch", "leaf")).forEach( - (key) => { - if (key !== this.selectedMenuItem) { - if (root[key].branch) { - stack.push(key); - isActiveInternal(root[key]); - stack.pop(key); - } - } else { + this.getMenuItems(root).forEach((key) => { + if (key !== this.selectedMenuItem) { + if (root[key].branch) { stack.push(key); - path = [...stack.arr]; + isActiveInternal(root[key]); + stack.pop(key); } + } else { + stack.push(key); + path = [...stack.arr]; } - ); + }); }; isActiveInternal(this.menu); @@ -149,17 +145,17 @@ export class MenuSelection { } } -export function getMenuItemUrlPath(MENU, item) { +export function getNameByPath(MENU, path) { const root = MENU; - let path; + let name; Object.keys(root).forEach((key) => { if (_.isObject(root[key])) { - path = getMenuItemUrlPath(root[key], item); - } else if (key === item) { - path = root[key]; + name = getNameByPath(root[key], path); + } else if (root[key] === path) { + name = key; } }); - return path; + return name; } diff --git a/src/utils/pagination.js b/src/utils/pagination.js index 8f0adf6..292d7fe 100644 --- a/src/utils/pagination.js +++ b/src/utils/pagination.js @@ -10,11 +10,11 @@ export function pageIndexToPage(pageIndex) { * @param {any} response Web APIs Response * @return {Object} pagination data */ -export function getResponseHeaders(reponse) { +export function getResponseHeaders(response) { return { - page: +(reponse.headers.get("X-Page") || 0), - perPage: +(reponse.headers.get("X-Per-Page") || 0), - total: +(reponse.headers.get("X-Total") || 0), - totalPages: +(reponse.headers.get("X-Total-Pages") || 0), + page: +(response.headers.get("X-Page") || 0), + perPage: +(response.headers.get("X-Per-Page") || 0), + total: +(response.headers.get("X-Total") || 0), + totalPages: +(response.headers.get("X-Total-Pages") || 0), }; } diff --git a/src/utils/tag.js b/src/utils/tag.js index 39bea25..2b4418a 100644 --- a/src/utils/tag.js +++ b/src/utils/tag.js @@ -6,7 +6,7 @@ export function calculateNumberOfVisibleTags(tags) { let n = tags.length; if (tagsString.length > MAX_LEN) { let ss = ""; - for (n = 0; n < tags.length && ss.length < 20; n += 1) { + for (n = 0; n < tags.length && ss.length < MAX_LEN; n += 1) { ss = ss.concat(tags[n]); } } diff --git a/src/utils/url.js b/src/utils/url.js index bffc6d1..2e77487 100644 --- a/src/utils/url.js +++ b/src/utils/url.js @@ -8,7 +8,7 @@ import qs from "qs"; * `{ p: undefined }` => "" * `{ p: value }` => "p=value" * `{ p: [] }` => "" - * `{ p: ['Challenge', 'First2Finish', 'Task'] } => "p[]=Challenge&p[]=First2Finish&p[]=Taks` + * `{ p: ['Challenge', 'First2Finish', 'Task'] } => "p[]=Challenge&p[]=First2Finish&p[]=Task` * `{ p: ['Design', 'Development', 'Data Science', 'Quality Assurance'] }` => "p[]=Design&p[]=Development&p=Data%20Science&p[]=Quality%20Assurance" * `{ p: { Des: true, Dev: true, DS: false, QA: false } }` => "p[Des]=true&p[Dev]=true&p[DS]=false&p[QA]=false" * @@ -26,3 +26,16 @@ export function buildQueryString(params) { return queryString; } + +export function parseUrlQuery(queryString) { + return qs.parse(queryString, { ignoreQueryPrefix: true }); +} + +export function updateQuery(params) { + const oldQuery = decodeURIComponent(window.location.search); + let query = buildQueryString(params); + query = `?${query.substring(1).split("&").sort().join("&")}`; + if (query !== oldQuery) { + window.history.pushState(window.history.state, "", query); + } +} diff --git a/webpack.config.js b/webpack.config.js index 2a11667..81b9285 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -16,6 +16,9 @@ module.exports = (webpackConfigEnv) => { disableHtmlGeneration: true, }); + const unusedFilesWebpackPlugin = defaultConfig.plugins.find(p => p.constructor.name === "UnusedFilesWebpackPlugin"); + unusedFilesWebpackPlugin.globOptions.ignore.push("**/assets/icons/*.svg", "**/__mocks__/**"); + let cssLocalIdent; if (process.env.APPMODE == 'development') { cssLocalIdent = 'earn_[path][name]___[local]___[hash:base64:6]'; @@ -66,7 +69,7 @@ module.exports = (webpackConfigEnv) => { }, { /* Loads scss stylesheets. */ - test: /\.scss/, + test: /\.scss$/, use: [ 'style-loader', { @@ -94,14 +97,6 @@ module.exports = (webpackConfigEnv) => { }, } ] - }, - { - /* Loads css stylesheets */ - test: /\.css$/, - use: [ - 'style-loader', - 'css-loader', - ], } ], },