diff --git a/package-lock.json b/package-lock.json index 80fae0c..0def7a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,32 +13,31 @@ } }, "@fortawesome/fontawesome-common-types": { - "version": "0.2.24", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.24.tgz", - "integrity": "sha512-IPBT/1LdUVQpHcqdrh8uI2/86Fbu7933hkA/HweiCmP5QgF/8PecFM00gYvykxf0RZud8bg8zu+YfggDFUc1Kw==" + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.25.tgz", + "integrity": "sha512-3RuZPDuuPELd7RXtUqTCfed14fcny9UiPOkdr2i+cYxBoTOfQgxcDoq77fHiiHcgWuo1LoBUpvGxFF1H/y7s3Q==" }, "@fortawesome/fontawesome-svg-core": { - "version": "1.2.24", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.24.tgz", - "integrity": "sha512-9uVGOEZwviZKbkOVX8nn8cErVqOHBAd1Fqd2OH7Iwu0vxGWdb3fFOMhaAyMXUHZpq1u5C9/HClCV49ci4WmJAg==", + "version": "1.2.25", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.25.tgz", + "integrity": "sha512-MotKnn53JKqbkLQiwcZSBJVYtTgIKFbh7B8+kd05TSnfKYPFmjKKI59o2fpz5t0Hzl35vVGU6+N4twoOpZUrqA==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.24" + "@fortawesome/fontawesome-common-types": "^0.2.25" } }, "@fortawesome/free-solid-svg-icons": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.1.tgz", - "integrity": "sha512-bB3hXON1K6mVOetTTg5VXZ4CAHg866p7MqenDkJ/eVcbWbGQRE45ojHEwkf37tWx3E8z6lcEameRwU9r5tGwjg==", + "version": "5.11.2", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.11.2.tgz", + "integrity": "sha512-zBue4i0PAZJUXOmLBBvM7L0O7wmsDC8dFv9IhpW5QL4kT9xhhVUsYg/LX1+5KaukWq4/cbDcKT+RT1aRe543sg==", "requires": { - "@fortawesome/fontawesome-common-types": "^0.2.24" + "@fortawesome/fontawesome-common-types": "^0.2.25" } }, "@fortawesome/react-fontawesome": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.4.tgz", - "integrity": "sha512-GwmxQ+TK7PEdfSwvxtGnMCqrfEm0/HbRHArbUudsYiy9KzVCwndxa2KMcfyTQ8El0vROrq8gOOff09RF1oQe8g==", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.5.tgz", + "integrity": "sha512-WYDKTgyAWOncujWhhzhW7k8sgO5Eo2pZTUL51yNzSQNBUwwr6rNKg/JUSE3iebaU1XShHw74aKc1kJ+jvtRNew==", "requires": { - "humps": "^2.0.1", "prop-types": "^15.5.10" } }, @@ -189,9 +188,9 @@ } }, "@types/lodash": { - "version": "4.14.138", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.138.tgz", - "integrity": "sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg==", + "version": "4.14.141", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.141.tgz", + "integrity": "sha512-v5NYIi9qEbFEUpCyikmnOYe4YlP8BMUdTcNCAquAKzu+FA7rZ1onj9x80mbnDdOW/K5bFf3Tv5kJplP33+gAbQ==", "dev": true }, "@types/mime": { @@ -222,9 +221,9 @@ "dev": true }, "@types/prop-types": { - "version": "15.7.1", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.1.tgz", - "integrity": "sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==", + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", "dev": true }, "@types/range-parser": { @@ -234,9 +233,9 @@ "dev": true }, "@types/react": { - "version": "16.9.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.2.tgz", - "integrity": "sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg==", + "version": "16.9.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.5.tgz", + "integrity": "sha512-jQ12VMiFOWYlp+j66dghOWcmDDwhca0bnlcTxS4Qz/fh5gi6wpaZDthPEu/Gc/YlAuO87vbiUXL8qKstFvuOaA==", "dev": true, "requires": { "@types/prop-types": "*", @@ -253,18 +252,18 @@ } }, "@types/react-dom": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.0.tgz", - "integrity": "sha512-OL2lk7LYGjxn4b0efW3Pvf2KBVP0y1v3wip1Bp7nA79NkOpElH98q3WdCEdDj93b2b0zaeBG9DvriuKjIK5xDA==", + "version": "16.9.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.1.tgz", + "integrity": "sha512-1S/akvkKr63qIUWVu5IKYou2P9fHLb/P2VAwyxVV85JGaGZTcUniMiTuIqM3lXFB25ej6h+CYEQ27ERVwi6eGA==", "dev": true, "requires": { "@types/react": "*" } }, "@types/react-redux": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.2.tgz", - "integrity": "sha512-Iim6UCtD0mZX9U3jBuT6ZObBZ8UlakoOgefiRgi5wakfbNnXd3TUwwUMgi3Ijc0fxsPLZ5ULoz0oDy15YIaLmQ==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.4.tgz", + "integrity": "sha512-SUV/7d+4L7C1Db/D4pqASgN1V1U2HnDEhEol9lYpPSguS76xFboZzf5ha2hTz6v31cUewyC7WksMh1q8JxhebQ==", "dev": true, "requires": { "@types/hoist-non-react-statics": "^3.3.0", @@ -274,9 +273,9 @@ } }, "@types/react-router": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.0.3.tgz", - "integrity": "sha512-j2Gge5cvxca+5lK9wxovmGPgpVJMwjyu5lTA/Cd6fLGoPq7FXcUE1jFkEdxeyqGGz8VfHYSHCn5Lcn24BzaNKA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.1.tgz", + "integrity": "sha512-S7SlFAPb7ZKr6HHMW0kLHGcz8pyJSL0UdM+JtlWthDqKUWwr7E6oPXuHgkofDI8dKCm16slg8K8VCf5pZJquaA==", "dev": true, "requires": { "@types/history": "*", @@ -284,9 +283,9 @@ } }, "@types/react-router-dom": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-4.3.5.tgz", - "integrity": "sha512-eFajSUASYbPHg2BDM1G8Btx+YqGgvROPIg6sBhl3O4kbDdYXdFdfrgQFf/pcBuQVObjfT9AL/dd15jilR5DIEA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.0.tgz", + "integrity": "sha512-YCh8r71pL5p8qDwQf59IU13hFy/41fDQG/GeOI3y+xmD4o0w3vEPxE8uBe+dvOgMoDl0W1WUZsWH0pxc1mcZyQ==", "dev": true, "requires": { "@types/history": "*", @@ -372,9 +371,9 @@ } }, "@types/webpack": { - "version": "4.39.1", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.1.tgz", - "integrity": "sha512-rgO9ihNu/l72Sjx3shqwc9r6gi+tOMsqxhMEZhOEVIZt82GFOeUyEdpTk1BO2HqEHLS/XJW8ldUTIIfIMMyYFQ==", + "version": "4.39.2", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.2.tgz", + "integrity": "sha512-3c7+vcmyyIi3RBoOdXs8k3E9rQVIy6yOBqK0DFk6lnJ76JUfbDBWbEf1JflzyPQf56W4ToE+2YPnbxbucniW5w==", "dev": true, "requires": { "@types/anymatch": "*", @@ -383,14 +382,6 @@ "@types/uglify-js": "*", "@types/webpack-sources": "*", "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "@types/webpack-bundle-analyzer": { @@ -424,14 +415,6 @@ "@types/node": "*", "@types/source-list-map": "*", "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } } }, "@types/zxcvbn": { @@ -884,9 +867,9 @@ "dev": true }, "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", "dev": true }, "array-union": { @@ -1133,9 +1116,9 @@ } }, "bluebird": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz", - "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==", + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.0.tgz", + "integrity": "sha512-aBQ1FxIa7kSWCcmKHlcHFlT2jt6J/l4FzC7KcPELkOJOsPOb/bccdhmIrKDfXhwFrmc7vDoDrrepFvGqjyXGJg==", "dev": true }, "bn.js": { @@ -1160,14 +1143,6 @@ "qs": "6.7.0", "raw-body": "2.4.0", "type-is": "~1.6.17" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - } } }, "bonjour": { @@ -1182,6 +1157,14 @@ "dns-txt": "^2.0.2", "multicast-dns": "^6.0.1", "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "dev": true + } } }, "boolbase": { @@ -1328,9 +1311,9 @@ "dev": true }, "bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", "dev": true }, "cacache": { @@ -1520,9 +1503,9 @@ } }, "chownr": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.2.tgz", - "integrity": "sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==", "dev": true }, "chrome-trace-event": { @@ -1652,9 +1635,9 @@ } }, "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", + "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", "dev": true }, "commondir": { @@ -1691,6 +1674,14 @@ "on-headers": "~1.0.2", "safe-buffer": "5.1.2", "vary": "~1.1.2" + }, + "dependencies": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", + "dev": true + } } }, "concat-map": { @@ -1925,9 +1916,9 @@ "dev": true }, "csstype": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.6.tgz", - "integrity": "sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==", + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz", + "integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ==", "dev": true }, "currently-unhandled": { @@ -2532,14 +2523,6 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" - }, - "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=", - "dev": true - } } }, "extend": { @@ -4219,11 +4202,6 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "dev": true }, - "humps": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/humps/-/humps-2.0.1.tgz", - "integrity": "sha1-3QLqYIG9BWjcXQcxhEY5V7qe+ao=" - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -4368,9 +4346,9 @@ "dev": true }, "is-absolute-url": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.2.tgz", - "integrity": "sha512-+5g/wLlcm1AcxSP7014m6GvbPHswDx980vD/3bZaap8aGV9Yfs7Q6y6tfaupgZ5O74Byzc8dGrSCJ+bFXx0KdA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz", + "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==", "dev": true }, "is-accessor-descriptor": { @@ -5190,9 +5168,9 @@ } }, "node-forge": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.8.2.tgz", - "integrity": "sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg==", + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", "dev": true }, "node-gyp": { @@ -6178,14 +6156,6 @@ "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", - "dev": true - } } }, "re-reselect": { @@ -6194,9 +6164,9 @@ "integrity": "sha512-JsecfN+JlckncVXTWFWjn0Vk6uInl8GSf4eEd9tTk5qXHlgqkPdILpnYpgZcISXNYAzvfvsCZviaDk8AxyS5sg==" }, "react": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/react/-/react-16.9.0.tgz", - "integrity": "sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react/-/react-16.10.2.tgz", + "integrity": "sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -6212,14 +6182,14 @@ } }, "react-dom": { - "version": "16.9.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.9.0.tgz", - "integrity": "sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ==", + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.10.2.tgz", + "integrity": "sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", "prop-types": "^15.6.2", - "scheduler": "^0.15.0" + "scheduler": "^0.16.2" } }, "react-is": { @@ -6241,9 +6211,9 @@ } }, "react-router": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz", - "integrity": "sha512-EM7suCPNKb1NxcTZ2LEOWFtQBQRQXecLxVpdsP4DW4PbbqYWeRiLyV/Tt1SdCrvT2jcyXAXmVTmzvSzrPR63Bg==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.1.2.tgz", + "integrity": "sha512-yjEuMFy1ONK246B+rsa0cUam5OeAQ8pyclRDgpxuSCrAlJ1qN9uZ5IgyKC7gQg0w8OM50NXHEegPh/ks9YuR2A==", "requires": { "@babel/runtime": "^7.1.2", "history": "^4.9.0", @@ -6273,15 +6243,15 @@ } }, "react-router-dom": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.0.1.tgz", - "integrity": "sha512-zaVHSy7NN0G91/Bz9GD4owex5+eop+KvgbxXsP/O+iW1/Ln+BrJ8QiIR5a6xNPtrdTvLkxqlDClx13QO1uB8CA==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.1.2.tgz", + "integrity": "sha512-7BPHAaIwWpZS074UKaw1FjVdZBSVWEk8IuDXdB+OkLb8vd/WRQIpA4ag9WQk61aEfQs47wHyjWUoUGGZxpQXew==", "requires": { "@babel/runtime": "^7.1.2", "history": "^4.9.0", "loose-envify": "^1.3.1", "prop-types": "^15.6.2", - "react-router": "5.0.1", + "react-router": "5.1.2", "tiny-invariant": "^1.0.2", "tiny-warning": "^1.0.0" } @@ -6995,9 +6965,9 @@ } }, "scheduler": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz", - "integrity": "sha512-xAefmSfN6jqAa7Kuq7LIJY0bwAPG3xlCj0HMEBQk1lxYiDKZscY2xJ5U/61ZTrYbmNQbXa+gc7czPkVo11tnCg==", + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.16.2.tgz", + "integrity": "sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" @@ -7042,12 +7012,12 @@ "dev": true }, "selfsigned": { - "version": "1.10.6", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.6.tgz", - "integrity": "sha512-i3+CeqxL7DpAazgVpAGdKMwHuL63B5nhJMh9NQ7xmChGkA3jNFflq6Jyo1LLJYcr3idWiNOPWHCrm4zMayLG4w==", + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", "dev": true, "requires": { - "node-forge": "0.8.2" + "node-forge": "0.9.0" } }, "semver": { @@ -7779,9 +7749,9 @@ } }, "terser": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.1.tgz", - "integrity": "sha512-pnzH6dnFEsR2aa2SJaKb1uSCl3QmIsJ8dEkj0Fky+2AwMMcC9doMqLOQIH6wVTEKaVfKVvLSk5qxPBEZT9mywg==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.3.8.tgz", + "integrity": "sha512-otmIRlRVmLChAWsnSFNO0Bfk6YySuBp6G9qrHiJwlLDd4mxe2ta4sjI7TzIR+W1nBMjilzrMcPOz9pSusgx3hQ==", "dev": true, "requires": { "commander": "^2.20.0", @@ -7940,9 +7910,9 @@ "dev": true }, "ts-loader": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.1.2.tgz", - "integrity": "sha512-dudxFKm0Ellrg/gLNlu+97/UgwvoMK0SdUVImPUSzq3IcRUVtShylZvcMX+CgvCQL1BEKb913NL0gAP1GA/OFw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.2.0.tgz", + "integrity": "sha512-Da8h3fD+HiZ9GvZJydqzk3mTC9nuOKYlJcpuk+Zv6Y1DPaMvBL+56GRzZFypx2cWrZFMsQr869+Ua2slGoLxvQ==", "dev": true, "requires": { "chalk": "^2.3.0", @@ -8296,9 +8266,9 @@ } }, "webpack": { - "version": "4.40.2", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.40.2.tgz", - "integrity": "sha512-5nIvteTDCUws2DVvP9Qe+JPla7kWPPIDFZv55To7IycHWZ+Z5qBdaBYPyuXWdhggTufZkQwfIK+5rKQTVovm2A==", + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.0.tgz", + "integrity": "sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g==", "dev": true, "requires": { "@webassemblyjs/ast": "1.8.5", @@ -8432,9 +8402,9 @@ } }, "webpack-bundle-analyzer": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.1.tgz", - "integrity": "sha512-CDdaT3TTu4F9X3tcDq6PNJOiNGgREOM0WdN2vVAoUUn+M6NLB5kJ543HImCWbrDwOpbpGARSwU8r+u0Pl367kA==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.5.2.tgz", + "integrity": "sha512-g9spCNe25QYUVqHRDkwG414GTok2m7pTTP0wr6l0J50Z3YLS04+BGodTqqoVBL7QfU/U/9p/oiI5XFOyfZ7S/A==", "dev": true, "requires": { "acorn": "^6.0.7", @@ -8483,9 +8453,9 @@ } }, "webpack-dev-middleware": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.1.tgz", - "integrity": "sha512-5MWu9SH1z3hY7oHOV6Kbkz5x7hXbxK56mGHNqHTe6d+ewxOwKUxoUJBs7QIaJb33lPjl9bJZ3X0vCoooUzC36A==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", "dev": true, "requires": { "memory-fs": "^0.4.1", @@ -8504,9 +8474,9 @@ } }, "webpack-dev-server": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.1.tgz", - "integrity": "sha512-9F5DnfFA9bsrhpUCAfQic/AXBVHvq+3gQS+x6Zj0yc1fVVE0erKh2MV4IV12TBewuTrYeeTIRwCH9qLMvdNvTw==", + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz", + "integrity": "sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ==", "dev": true, "requires": { "ansi-html": "0.0.7", @@ -8518,18 +8488,18 @@ "del": "^4.1.1", "express": "^4.17.1", "html-entities": "^1.2.1", - "http-proxy-middleware": "^0.19.1", + "http-proxy-middleware": "0.19.1", "import-local": "^2.0.0", "internal-ip": "^4.3.0", "ip": "^1.1.5", - "is-absolute-url": "^3.0.2", + "is-absolute-url": "^3.0.3", "killable": "^1.0.1", "loglevel": "^1.6.4", "opn": "^5.5.0", "p-retry": "^3.0.1", "portfinder": "^1.0.24", "schema-utils": "^1.0.0", - "selfsigned": "^1.10.6", + "selfsigned": "^1.10.7", "semver": "^6.3.0", "serve-index": "^1.9.1", "sockjs": "0.3.19", @@ -8538,7 +8508,7 @@ "strip-ansi": "^3.0.1", "supports-color": "^6.1.0", "url": "^0.11.0", - "webpack-dev-middleware": "^3.7.1", + "webpack-dev-middleware": "^3.7.2", "webpack-log": "^2.0.0", "ws": "^6.2.1", "yargs": "12.0.5" @@ -8858,9 +8828,9 @@ "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "yargs": { diff --git a/package.json b/package.json index dc4535a..71ca8bb 100644 --- a/package.json +++ b/package.json @@ -18,16 +18,16 @@ "devDependencies": { "@types/classnames": "^2.2.9", "@types/html-webpack-plugin": "^3.2.1", - "@types/lodash": "^4.14.138", + "@types/lodash": "^4.14.141", "@types/mini-css-extract-plugin": "^0.8.0", - "@types/react": "^16.9.2", + "@types/react": "^16.9.5", "@types/react-avatar-editor": "^10.3.4", - "@types/react-dom": "^16.9.0", - "@types/react-redux": "^7.1.2", - "@types/react-router-dom": "^4.3.5", + "@types/react-dom": "^16.9.1", + "@types/react-redux": "^7.1.4", + "@types/react-router-dom": "^5.1.0", "@types/redux-logger": "^3.0.7", "@types/uuid": "^3.4.5", - "@types/webpack": "^4.39.1", + "@types/webpack": "^4.39.2", "@types/webpack-bundle-analyzer": "^2.13.3", "@types/webpack-dev-server": "^3.1.7", "@types/zxcvbn": "^4.4.0", @@ -39,29 +39,29 @@ "npm-run-all": "^4.1.5", "sass-loader": "^8.0.0", "style-loader": "^1.0.0", - "ts-loader": "^6.1.2", + "ts-loader": "^6.2.0", "ts-node": "^8.4.1", "typescript": "^3.6.3", - "webpack": "^4.40.2", - "webpack-bundle-analyzer": "^3.5.1", + "webpack": "^4.41.0", + "webpack-bundle-analyzer": "^3.5.2", "webpack-cli": "^3.3.9", - "webpack-dev-server": "^3.8.1" + "webpack-dev-server": "^3.8.2" }, "dependencies": { - "@fortawesome/fontawesome-common-types": "^0.2.24", - "@fortawesome/fontawesome-svg-core": "^1.2.24", - "@fortawesome/free-solid-svg-icons": "^5.11.1", - "@fortawesome/react-fontawesome": "^0.1.4", + "@fortawesome/fontawesome-common-types": "^0.2.25", + "@fortawesome/fontawesome-svg-core": "^1.2.25", + "@fortawesome/free-solid-svg-icons": "^5.11.2", + "@fortawesome/react-fontawesome": "^0.1.5", "classnames": "^2.2.6", "history": "^4.10.1", "lodash": "^4.17.15", "moment": "^2.24.0", "re-reselect": "^3.4.0", - "react": "^16.9.0", + "react": "^16.10.2", "react-avatar-editor": "^11.0.7", - "react-dom": "^16.9.0", + "react-dom": "^16.10.2", "react-redux": "^7.1.1", - "react-router-dom": "^5.0.1", + "react-router-dom": "^5.1.2", "redux": "^4.0.4", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", diff --git a/src/actions/apps.ts b/src/actions/apps.ts new file mode 100644 index 0000000..b724aa6 --- /dev/null +++ b/src/actions/apps.ts @@ -0,0 +1,48 @@ +import { apiFetch } from 'src/api' +import { setEntities } from 'src/actions/entities' +import { startRequest, finishRequest } from 'src/actions/requests' +import { objectToQuerystring } from 'src/utils' +import { normalize } from 'src/utils/normalization' + +import { AppThunkAction, RequestKey, EntityType, App }from 'src/types' + +interface AppsResponse { + apps: App[] + continuation?: string +} + +export const fetchApps = (sort?: string, continuation?: string): AppThunkAction => async dispatch => { + dispatch(startRequest(RequestKey.FetchApps)) + + try { + const response = await apiFetch({ + path: `/api/apps?${objectToQuerystring({ sort, continuation })}`, + }) + + const apps = normalize(response.apps, EntityType.App) + + dispatch(setEntities(apps.entities)) + dispatch(finishRequest(RequestKey.FetchApps, true)) + } catch (err) { + dispatch(finishRequest(RequestKey.FetchApps, false)) + throw err + } +} + +export const fetchSelfApps = (sort?: string): AppThunkAction => async dispatch => { + dispatch(startRequest(RequestKey.FetchSelfApps)) + + try { + const response = await apiFetch({ + path: `/api/self/apps?${objectToQuerystring({ sort })}`, + }) + + const apps = normalize(response.apps, EntityType.App) + + dispatch(setEntities(apps.entities)) + dispatch(finishRequest(RequestKey.FetchSelfApps, true)) + } catch (err) { + dispatch(finishRequest(RequestKey.FetchSelfApps, false)) + throw err + } +} diff --git a/src/actions/directory.ts b/src/actions/groups.ts similarity index 94% rename from src/actions/directory.ts rename to src/actions/groups.ts index 8b6bd15..545bba5 100644 --- a/src/actions/directory.ts +++ b/src/actions/groups.ts @@ -9,34 +9,34 @@ import { normalize } from 'src/utils/normalization' import { AppThunkAction, Entity, RequestKey, EntityType, User } from 'src/types' export interface SetGroupsAction extends Action { - type: 'DIRECTORY_SET_GROUPS' + type: 'GROUPS_SET_GROUPS' payload: string[] } export interface AppendGroupsAction extends Action { - type: 'DIRECTORY_APPEND_GROUPS', + type: 'GROUPS_APPEND_GROUPS', payload: string[] } export interface SetContinuationAction extends Action { - type: 'DIRECTORY_SET_CONTINUATION' + type: 'GROUPS_SET_CONTINUATION' payload: string } -export type DirectoryActions = SetGroupsAction | AppendGroupsAction | SetContinuationAction +export type GroupsActions = SetGroupsAction | AppendGroupsAction | SetContinuationAction export const setGroups = (groups: string[]): SetGroupsAction => ({ - type: 'DIRECTORY_SET_GROUPS', + type: 'GROUPS_SET_GROUPS', payload: groups, }) export const appendGroups = (groups: string[]): AppendGroupsAction => ({ - type: 'DIRECTORY_APPEND_GROUPS', + type: 'GROUPS_APPEND_GROUPS', payload: groups, }) export const setContinuation = (continuation: string): SetContinuationAction => ({ - type: 'DIRECTORY_SET_CONTINUATION', + type: 'GROUPS_SET_CONTINUATION', payload: continuation, }) diff --git a/src/components/app.tsx b/src/components/app.tsx new file mode 100644 index 0000000..deb5807 --- /dev/null +++ b/src/components/app.tsx @@ -0,0 +1,106 @@ +import React, { FC, useEffect } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom' + +import { fetchSelf, setChecked } from 'src/actions/authentication' +import { getFetching } from 'src/selectors' +import { getCollapsed } from 'src/selectors/menu' + +import { LOCAL_STORAGE_ACCESS_TOKEN_KEY } from 'src/constants' +import { AppState, AppThunkDispatch } from 'src/types' + +import Footer from './footer' +import NavigationMenu from './navigation-menu' +import NotificationContainer from './notification-container' +import Spinner from './spinner' +import UserInfo from './user-info' + +import About from './pages/about' +import Developers from './pages/developers' +import Group from './pages/group' +import GroupAdmin from './pages/group-admin' +import Groups from './pages/groups' +import Home from './pages/home' +import Login from './pages/login' +import Register from './pages/register' +import RegisterGroup from './pages/register-group' +import Self from './pages/self' + +import '../styles/app.scss' +import '../styles/spinner.scss' + +const App: FC = () => { + const collapsed = useSelector(getCollapsed) + const fetching = useSelector(getFetching) + + const dispatch = useDispatch() + + const mainMenuWidth = 275 + const mainColumnMargin = collapsed ? 0 : mainMenuWidth + + useEffect(() => { + if (localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)) { + dispatch(fetchSelf()) + } else { + dispatch(setChecked()) + } + }, []) + + return ( + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ ) +} + +export default App diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx deleted file mode 100644 index 760f4b5..0000000 --- a/src/components/app/app.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import React, { FC, useEffect } from 'react' -import { BrowserRouter as Router, Route, Link } from 'react-router-dom' - -import { LOCAL_STORAGE_ACCESS_TOKEN_KEY } from 'src/constants' - -import Footer from '../footer' -import NavigationMenu from '../navigation-menu' -import NotificationContainer from '../notification-container' -import Spinner from '../spinner' -import UserInfo from '../user-info' - -import About from '../pages/about' -import Developers from '../pages/developers' -import Directory from '../pages/directory' -import Group from '../pages/group' -import GroupAdmin from '../pages/group-admin' -import Home from '../pages/home' -import Login from '../pages/login' -import Register from '../pages/register' -import RegisterGroup from '../pages/register-group' -import Self from '../pages/self' - -import './app.scss' - -interface Props { - collapsed: boolean - fetching: boolean - fetchSelf: () => void - setChecked: () => void -} - -const App: FC = ({ collapsed, fetching, fetchSelf, setChecked }) => { - const mainMenuWidth = 275 - const mainColumnMargin = collapsed ? 0 : mainMenuWidth - - useEffect(() => { - if (localStorage.getItem(LOCAL_STORAGE_ACCESS_TOKEN_KEY)) { - fetchSelf() - } else { - setChecked() - } - }, []) - - return ( - -
- - -
- - - - - - - - - - -
- - -
-
- ) -} - -export default App diff --git a/src/components/app/index.ts b/src/components/app/index.ts deleted file mode 100644 index 6898745..0000000 --- a/src/components/app/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { connect } from 'react-redux' - -import { fetchSelf, setChecked } from 'src/actions/authentication' -import { getFetching } from 'src/selectors' -import { getCollapsed } from 'src/selectors/menu' - -import { AppState, AppThunkDispatch } from 'src/types' - -import App from './app' - -const mapStateToProps = (state: AppState) => ({ - collapsed: getCollapsed(state), - fetching: getFetching(state), -}) - -const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({ - fetchSelf: () => { - dispatch(fetchSelf()) - }, - setChecked: () => { - dispatch(setChecked()) - } -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(App) diff --git a/src/components/create-group-form/create-group-form.tsx b/src/components/create-group-form.tsx similarity index 76% rename from src/components/create-group-form/create-group-form.tsx rename to src/components/create-group-form.tsx index 7daf535..4bf1a82 100644 --- a/src/components/create-group-form/create-group-form.tsx +++ b/src/components/create-group-form.tsx @@ -1,17 +1,24 @@ import React, { FC, FocusEventHandler } from 'react' +import { useDispatch } from 'react-redux' import { Link } from 'react-router-dom' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { faUpload, faIdCard } from '@fortawesome/free-solid-svg-icons' -import CheckboxField from '../forms/checkbox-field' -import TextField from '../forms/text-field' -import SelectField from '../forms/select-field' +import { checkGroupAvailability } from 'src/actions/registration' -interface Props { - checkAvailability: FocusEventHandler -} +import CheckboxField from './forms/checkbox-field' +import TextField from './forms/text-field' +import SelectField from './forms/select-field' + +const CreateGroupForm: FC = () => { + const dispatch = useDispatch() + + const checkAvailability = (value: string) => { + if (value.length > 3) { + dispatch(checkGroupAvailability(value)) + } + } -const CreateGroupForm: FC = ({ checkAvailability }) => { const registrationOptions = { open: 'Anyone can join', approval: 'Users must be approved', @@ -20,7 +27,7 @@ const CreateGroupForm: FC = ({ checkAvailability }) => { return (
- + checkAvailability(e.target.value)} />
diff --git a/src/components/create-group-form/index.ts b/src/components/create-group-form/index.ts deleted file mode 100644 index 3dfc395..0000000 --- a/src/components/create-group-form/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { FocusEventHandler } from 'react' -import { connect } from 'react-redux' - -import { checkGroupAvailability } from 'src/actions/registration' -import { AppThunkDispatch } from 'src/types' - -import CreateGroupForm from './create-group-form' - -const mapDispatchToProps = (dispatch: AppThunkDispatch) => { - const checkAvailability: FocusEventHandler = event => { - const value = event.target.value - - if (value.length > 3) { - dispatch(checkGroupAvailability(event.target.value)) - } - } - - return { - checkAvailability, - } -} - -export default connect( - null, - mapDispatchToProps -)(CreateGroupForm) diff --git a/src/components/create-group-step.tsx b/src/components/create-group-step.tsx new file mode 100644 index 0000000..fbecb31 --- /dev/null +++ b/src/components/create-group-step.tsx @@ -0,0 +1,83 @@ +import React, { FC } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faBuilding, faArrowLeft } from '@fortawesome/free-solid-svg-icons' + +import { setFieldNotification } from 'src/actions/forms' +import { showNotification } from 'src/actions/notifications' +import { setStep } from 'src/actions/registration' +import { getFieldValue } from 'src/selectors/forms' + +import { MAX_ID_LENGTH } from 'src/constants' +import { AppState, AppThunkDispatch, NotificationType } from 'src/types' + +import CreateGroupForm from './create-group-form' + +interface Props { + register: () => void +} + +const CreateGroupStep: FC = ({ register }) => { + const name = useSelector(state => getFieldValue(state, 'group-name', '')) + const registration = useSelector(state => getFieldValue(state, 'group-registration', '')) + const agree = useSelector(state => getFieldValue(state, 'group-agree', false)) + + const dispatch = useDispatch() + + const next = (name: string, registration: string, agree: boolean) => { + let invalid = false + + if (!name) { + dispatch(setFieldNotification('group-name', NotificationType.Error, 'This is required')) + invalid = true + } + + if (name.length > MAX_ID_LENGTH) { + dispatch(setFieldNotification('group-name', NotificationType.Error, `This must be less than ${MAX_ID_LENGTH} characters`)) + invalid = true + } + + if (!agree) { + dispatch(setFieldNotification('group-agree', NotificationType.Error, 'You must agree to the terms and conditions to continue')) + dispatch(showNotification(NotificationType.Error, 'You must agree to the terms and conditions to continue.')) + invalid = true + } + + if (invalid) return + register() + } + + return ( +
+
+ + + +
+ + +
+ + +
+ ) +} + +export default CreateGroupStep diff --git a/src/components/create-group-step/create-group-step.tsx b/src/components/create-group-step/create-group-step.tsx deleted file mode 100644 index 148e50e..0000000 --- a/src/components/create-group-step/create-group-step.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { FC } from 'react' -import noop from 'lodash/noop' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faBuilding, faArrowLeft } from '@fortawesome/free-solid-svg-icons' - -import CreateGroupForm from '../create-group-form' - -export interface Props { - name?: string - registration?: string - agree?: boolean - previous?: () => void - next?: (name: string, registration: string, agree: boolean) => void - register: () => void -} - -const CreateGroupStep: FC = ({ - name = '', - registration = '', - agree = false, - previous = noop, - next = noop, -}) => ( -
-
- - - -
- - -
- - -
-) - -export default CreateGroupStep diff --git a/src/components/create-group-step/index.ts b/src/components/create-group-step/index.ts deleted file mode 100644 index 7dc331f..0000000 --- a/src/components/create-group-step/index.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { connect } from 'react-redux' - -import { setFieldNotification } from 'src/actions/forms' -import { showNotification } from 'src/actions/notifications' -import { setStep } from 'src/actions/registration' -import { getFieldValue } from 'src/selectors/forms' -import { MAX_ID_LENGTH } from 'src/constants' -import { AppState, AppThunkDispatch, NotificationType } from 'src/types' - -import CreateGroupStep, { Props } from './create-group-step' - -const mapStateToProps = (state: AppState) => ({ - name: getFieldValue(state, 'group-name', ''), - registration: getFieldValue(state, 'group-registration', ''), - agree: getFieldValue(state, 'group-agree', false), -}) - -const mapDispatchToProps = (dispatch: AppThunkDispatch, ownProps: Props) => ({ - previous: () => { - dispatch(setStep(0)) - }, - next: (name: string, registration: string, agree: boolean) => { - let invalid = false - - if (!name) { - dispatch(setFieldNotification('group-name', NotificationType.Error, 'This is required')) - invalid = true - } - - if (name.length > MAX_ID_LENGTH) { - dispatch(setFieldNotification('group-name', NotificationType.Error, `This must be less than ${MAX_ID_LENGTH} characters`)) - invalid = true - } - - if (!agree) { - dispatch(setFieldNotification('group-agree', NotificationType.Error, 'You must agree to the terms and conditions to continue')) - dispatch(showNotification(NotificationType.Error, 'You must agree to the terms and conditions to continue.')) - invalid = true - } - - if (invalid) return - if (ownProps.register) ownProps.register() - }, -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CreateGroupStep) diff --git a/src/components/create-user-form/create-user-form.tsx b/src/components/create-user-form.tsx similarity index 57% rename from src/components/create-user-form/create-user-form.tsx rename to src/components/create-user-form.tsx index b251480..a89fbbb 100644 --- a/src/components/create-user-form/create-user-form.tsx +++ b/src/components/create-user-form.tsx @@ -1,19 +1,25 @@ -import React, { FC, FocusEventHandler } from 'react' +import React, { FC } from 'react' +import { useDispatch } from 'react-redux' import { Link } from 'react-router-dom' import { faEnvelope, faIdCard } from '@fortawesome/free-solid-svg-icons' +import { checkUserAvailability } from 'src/actions/registration' -import CheckboxField from '../forms/checkbox-field' -import TextField from '../forms/text-field' -import PasswordField from '../forms/password-field' +import CheckboxField from './forms/checkbox-field' +import TextField from './forms/text-field' +import PasswordField from './forms/password-field' -interface Props { - checkAvailability: FocusEventHandler -} +const CreateUserForm: FC = () => { + const dispatch = useDispatch() + + const checkAvailability = (value: string) => { + if (value.length > 3) { + dispatch(checkUserAvailability(value)) + } + } -const CreateUserForm: FC = ({ checkAvailability }) => { return (
- + checkAvailability(e.target.value)} />

diff --git a/src/components/create-user-form/index.ts b/src/components/create-user-form/index.ts deleted file mode 100644 index e08abe6..0000000 --- a/src/components/create-user-form/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { FocusEventHandler } from 'react' -import { connect } from 'react-redux' - -import { checkUserAvailability } from 'src/actions/registration' -import { AppThunkDispatch } from 'src/types' - -import CreateUserForm from './create-user-form' - -const mapDispatchToProps = (dispatch: AppThunkDispatch) => { - const checkAvailability: FocusEventHandler = event => { - const value = event.target.value - - if (value.length > 3) { - dispatch(checkUserAvailability(event.target.value)) - } - } - - return { - checkAvailability, - } -} - -export default connect( - null, - mapDispatchToProps -)(CreateUserForm) diff --git a/src/components/create-user-step.tsx b/src/components/create-user-step.tsx new file mode 100644 index 0000000..18f028f --- /dev/null +++ b/src/components/create-user-step.tsx @@ -0,0 +1,101 @@ +import React, { FC } from 'react' +import { useSelector, useDispatch } from 'react-redux' +import zxcvbn from 'zxcvbn' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faUser, faArrowRight } from '@fortawesome/free-solid-svg-icons' + +import { setFieldNotification } from 'src/actions/forms' +import { showNotification } from 'src/actions/notifications' +import { setStep } from 'src/actions/registration' +import { getFieldValue } from 'src/selectors/forms' +import { MAX_ID_LENGTH, MAX_NAME_LENGTH } from 'src/constants' +import { AppState, AppThunkDispatch, NotificationType } from 'src/types' + +import CreateUserForm from './create-user-form' + +const CreateUserStep: FC = () => { + const userId = useSelector(state => getFieldValue(state, 'user-id', '')) + const name = useSelector(state => getFieldValue(state, 'user-name', '')) + const email = useSelector(state => getFieldValue(state, 'user-email', '')) + const password = useSelector(state => getFieldValue(state, 'password', '')) + const agree = useSelector(state => getFieldValue(state, 'user-agree', false)) + + const dispatch = useDispatch() + + const next = (userId: string, name: string, email: string, password: string, agree: boolean) => { + let invalid = false + + if (!userId) { + dispatch(setFieldNotification('user-id', NotificationType.Error, 'This is required')) + invalid = true + } + + if (userId.length > MAX_ID_LENGTH) { + dispatch(setFieldNotification('user-id', NotificationType.Error, `This must be less than ${MAX_ID_LENGTH} characters`)) + invalid = true + } + + if (name.length > MAX_NAME_LENGTH) { + dispatch(setFieldNotification('user-name', NotificationType.Error, `This must be less than ${MAX_NAME_LENGTH} characters`)) + invalid = true + } + + if (email === '') { + dispatch(setFieldNotification('user-email', NotificationType.Error, 'This is required')) + invalid = true + } + + if (!agree) { + dispatch(setFieldNotification('user-agree', NotificationType.Error, 'You must agree to the terms and conditions to continue')) + dispatch(showNotification(NotificationType.Error, 'You must agree to the terms and conditions to continue.')) + invalid = true + } + + if (password === '') { + dispatch(setFieldNotification('password', NotificationType.Error, 'This is required')) + invalid = true + } else { + const { score } = zxcvbn(password) + + if (score === 0) { + dispatch(setFieldNotification('password', NotificationType.Error, 'Try another password')) + invalid = true + } + } + + if (invalid) return + dispatch(setStep(1)) + } + + return ( +
+
+ + + +
+ + +
+ + +
+ ) +} + +export default CreateUserStep diff --git a/src/components/create-user-step/create-user-step.tsx b/src/components/create-user-step/create-user-step.tsx deleted file mode 100644 index 9b5dc3e..0000000 --- a/src/components/create-user-step/create-user-step.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React, { FC } from 'react' -import noop from 'lodash/noop' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' -import { faUser, faArrowRight } from '@fortawesome/free-solid-svg-icons' - -import CreateUserForm from '../create-user-form' - -export interface Props { - userId?: string - name?: string - email?: string - password?: string - agree?: boolean - next?: (userId: string, name: string, email: string, password: string, agree: boolean) => void -} - -const CreateUserStep: FC = ({ - userId = '', - name = '', - email = '', - password = '', - agree = false, - next = noop, -}) => ( -
-
- - - -
- - -
- - -
-) - -export default CreateUserStep diff --git a/src/components/create-user-step/index.ts b/src/components/create-user-step/index.ts deleted file mode 100644 index b84f46d..0000000 --- a/src/components/create-user-step/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { connect } from 'react-redux' -import zxcvbn from 'zxcvbn' - -import { setFieldNotification } from 'src/actions/forms' -import { showNotification } from 'src/actions/notifications' -import { setStep } from 'src/actions/registration' -import { getFieldValue } from 'src/selectors/forms' -import { MAX_ID_LENGTH, MAX_NAME_LENGTH } from 'src/constants' -import { AppState, AppThunkDispatch, NotificationType } from 'src/types' - -import CreateUserStep from './create-user-step' - -const mapStateToProps = (state: AppState) => ({ - userId: getFieldValue(state, 'user-id', ''), - name: getFieldValue(state, 'user-name', ''), - email: getFieldValue(state, 'user-email', ''), - password: getFieldValue(state, 'password', ''), - agree: getFieldValue(state, 'user-agree', false), -}) - -const mapDispatchToProps = (dispatch: AppThunkDispatch) => ({ - next: (userId: string, name: string, email: string, password: string, agree: boolean) => { - let invalid = false - - if (!userId) { - dispatch(setFieldNotification('user-id', NotificationType.Error, 'This is required')) - invalid = true - } - - if (userId.length > MAX_ID_LENGTH) { - dispatch(setFieldNotification('user-id', NotificationType.Error, `This must be less than ${MAX_ID_LENGTH} characters`)) - invalid = true - } - - if (name.length > MAX_NAME_LENGTH) { - dispatch(setFieldNotification('user-name', NotificationType.Error, `This must be less than ${MAX_NAME_LENGTH} characters`)) - invalid = true - } - - if (email === '') { - dispatch(setFieldNotification('user-email', NotificationType.Error, 'This is required')) - invalid = true - } - - if (!agree) { - dispatch(setFieldNotification('user-agree', NotificationType.Error, 'You must agree to the terms and conditions to continue')) - dispatch(showNotification(NotificationType.Error, 'You must agree to the terms and conditions to continue.')) - invalid = true - } - - if (password === '') { - dispatch(setFieldNotification('password', NotificationType.Error, 'This is required')) - invalid = true - } else { - const { score } = zxcvbn(password) - - if (score === 0) { - dispatch(setFieldNotification('password', NotificationType.Error, 'Try another password')) - invalid = true - } - } - - if (invalid) return - dispatch(setStep(1)) - }, -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CreateUserStep) diff --git a/src/components/footer/index.tsx b/src/components/footer.tsx similarity index 100% rename from src/components/footer/index.tsx rename to src/components/footer.tsx diff --git a/src/components/forms/checkbox-field.tsx b/src/components/forms/checkbox-field.tsx new file mode 100644 index 0000000..f2e0827 --- /dev/null +++ b/src/components/forms/checkbox-field.tsx @@ -0,0 +1,25 @@ +import React, { FC } from 'react' +import { useSelector, useDispatch } from 'react-redux' + +import { setFieldValue } from 'src/actions/forms' +import { getFieldValue } from 'src/selectors/forms' +import { AppState } from 'src/types' + +interface Props { + name: string +} + +const CheckboxField: FC = ({ name, children }) => { + const value = useSelector(state => getFieldValue(state, name, false)) + const dispatch = useDispatch() + + return ( + + ) +} + +export default CheckboxField diff --git a/src/components/forms/checkbox-field/checkbox-field.tsx b/src/components/forms/checkbox-field/checkbox-field.tsx deleted file mode 100644 index 3892041..0000000 --- a/src/components/forms/checkbox-field/checkbox-field.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import React, { FC } from 'react' -import noop from 'lodash/noop' - -export interface Props { - name: string - value?: boolean - setValue?: (value: boolean) => void -} - -const PasswordField: FC = ({ - value = false, - setValue = noop, - children, -}) => { - return ( - - ) -} - -export default PasswordField diff --git a/src/components/forms/checkbox-field/index.ts b/src/components/forms/checkbox-field/index.ts deleted file mode 100644 index 3fdd5c4..0000000 --- a/src/components/forms/checkbox-field/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Dispatch } from 'redux' -import { connect } from 'react-redux' - -import { setFieldValue } from 'src/actions/forms' -import { getFieldValue } from 'src/selectors/forms' -import { AppState } from 'src/types' - -import CheckboxField, { Props } from './checkbox-field' - -const mapStateToProps = (state: AppState, ownProps: Props) => ({ - value: getFieldValue(state, ownProps.name, false), -}) - -const mapDispatchToProps = (dispatch: Dispatch, ownProps: Props) => ({ - setValue: (value: boolean) => { - dispatch(setFieldValue(ownProps.name, value)) - } -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(CheckboxField) diff --git a/src/components/forms/password-field/password-field.tsx b/src/components/forms/password-field.tsx similarity index 80% rename from src/components/forms/password-field/password-field.tsx rename to src/components/forms/password-field.tsx index 1ca74fb..b475cc7 100644 --- a/src/components/forms/password-field/password-field.tsx +++ b/src/components/forms/password-field.tsx @@ -1,31 +1,32 @@ import React, { FC, ReactNode } from 'react' +import { useSelector, useDispatch } from 'react-redux' import classNames from 'classnames' -import noop from 'lodash/noop' import zxcvbn from 'zxcvbn' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { IconDefinition } from '@fortawesome/fontawesome-common-types' import { faKey, faExclamationTriangle, faCheckCircle } from '@fortawesome/free-solid-svg-icons' +import { setFieldValue } from 'src/actions/forms' +import { getFieldValue, getFieldNotification } from 'src/selectors/forms' import { notificationTypeToClassName } from 'src/utils' -import { FormNotification, ClassDictionary } from 'src/types' +import { AppState, FormNotification, ClassDictionary } from 'src/types' export interface Props { placeholder?: string userInputs?: string[] - value?: string - notification?: FormNotification showStrength?: boolean - setValue?: (value: string) => void } const PasswordField: FC = ({ placeholder, userInputs = [], - value = '', - notification, showStrength = true, - setValue = noop, }) => { + const value = useSelector(state => getFieldValue(state, 'password', '')) + const notification = useSelector(state => getFieldNotification(state, 'password')) + + const dispatch = useDispatch() + const inputClassDictionary: ClassDictionary = { input: true } const controlClassDictionary: ClassDictionary = { control: true, 'has-icons-left': true } const helpClassDictionary: ClassDictionary = { help: true } @@ -79,7 +80,12 @@ const PasswordField: FC = ({
- setValue(e.target.value)} /> + dispatch(setFieldValue('password', e.target.value))} /> diff --git a/src/components/forms/password-field/index.ts b/src/components/forms/password-field/index.ts deleted file mode 100644 index f773968..0000000 --- a/src/components/forms/password-field/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Dispatch } from 'redux' -import { connect } from 'react-redux' - -import { setFieldValue } from 'src/actions/forms' -import { getFieldValue, getFieldNotification } from 'src/selectors/forms' -import { AppState } from 'src/types' - -import PasswordField from './password-field' - -const mapStateToProps = (state: AppState) => ({ - value: getFieldValue(state, 'password', ''), - notification: getFieldNotification(state, 'password'), -}) - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - setValue: (value: string) => { - dispatch(setFieldValue('password', value)) - } -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(PasswordField) diff --git a/src/components/forms/select-field/select-field.tsx b/src/components/forms/select-field.tsx similarity index 72% rename from src/components/forms/select-field/select-field.tsx rename to src/components/forms/select-field.tsx index 14c8299..a9f8e35 100644 --- a/src/components/forms/select-field/select-field.tsx +++ b/src/components/forms/select-field.tsx @@ -1,34 +1,36 @@ import React, { FC } from 'react' +import { useSelector, useDispatch } from 'react-redux' import classNames from 'classnames' -import noop from 'lodash/noop' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { IconDefinition } from '@fortawesome/fontawesome-svg-core' +import { setFieldValue } from 'src/actions/forms' +import { getFieldValue, getFieldNotification } from 'src/selectors/forms' import { notificationTypeToClassName } from 'src/utils' -import { FormNotification, ClassDictionary } from 'src/types' +import { AppState, FormNotification, ClassDictionary } from 'src/types' interface SelectOptions { [value: string]: string } -export interface Props { +interface Props { name: string label: string options: SelectOptions - value?: string - notification?: FormNotification icon?: IconDefinition - setValue?: (value: string) => void } const SelectField: FC = ({ + name, label, options, - value, - notification, icon, - setValue = noop, }) => { + const value = useSelector(state => getFieldValue(state, name, '')) + const notification = useSelector(state => getFieldNotification(state, name)) + + const dispatch = useDispatch() + const opts = Object.entries(options) const controlClassDictionary: ClassDictionary = { control: true } const helpClassDictionary: ClassDictionary = { help: true } @@ -49,7 +51,7 @@ const SelectField: FC = ({
- dispatch(setFieldValue(name, e.target.value))}> {opts.map(([key, value]) => )}
diff --git a/src/components/forms/select-field/index.ts b/src/components/forms/select-field/index.ts deleted file mode 100644 index d41b7f5..0000000 --- a/src/components/forms/select-field/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Dispatch } from 'redux' -import { connect } from 'react-redux' - -import { setFieldValue } from 'src/actions/forms' -import { getFieldValue, getFieldNotification } from 'src/selectors/forms' -import { AppState } from 'src/types' - -import SelectField, { Props } from './select-field' - -const mapStateToProps = (state: AppState, ownProps: Props) => ({ - value: getFieldValue(state, ownProps.name, ''), - notification: getFieldNotification(state, ownProps.name), -}) - -const mapDispatchToProps = (dispatch: Dispatch, ownProps: Props) => ({ - setValue: (value: string) => { - dispatch(setFieldValue(ownProps.name, value)) - } -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(SelectField) diff --git a/src/components/forms/text-field/text-field.tsx b/src/components/forms/text-field.tsx similarity index 61% rename from src/components/forms/text-field/text-field.tsx rename to src/components/forms/text-field.tsx index c35e708..fd458c9 100644 --- a/src/components/forms/text-field/text-field.tsx +++ b/src/components/forms/text-field.tsx @@ -1,34 +1,37 @@ import React, { FC, FocusEventHandler } from 'react' +import { useSelector, useDispatch } from 'react-redux' import classNames from 'classnames' import noop from 'lodash/noop' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { IconDefinition } from '@fortawesome/fontawesome-common-types' +import { setFieldValue } from 'src/actions/forms' +import { getFieldValue, getFieldNotification } from 'src/selectors/forms' import { notificationTypeToClassName } from 'src/utils' -import { FormNotification, ClassDictionary } from 'src/types' +import { AppState, AppThunkDispatch, FormNotification, ClassDictionary } from 'src/types' -export interface Props { +interface Props { name: string label: string type?: 'text' | 'email' placeholder?: string icon?: IconDefinition - value?: string - notification?: FormNotification - setValue?: (value: string) => void - onBlur?: FocusEventHandler + onBlur?: FocusEventHandler } const TextField: FC = ({ + name, label, type = 'text', placeholder, icon, - value = '', - notification, - setValue = noop, onBlur = noop, }) => { + const value = useSelector(state => getFieldValue(state, name, '')) + const notification = useSelector(state => getFieldNotification(state, name)) + + const dispatch = useDispatch() + const controlClassDictionary = { control: true, 'has-icons-left': !!icon } const helpClassDictionary: ClassDictionary = { help: true } const inputClassDictionary: ClassDictionary = { input: true } @@ -44,7 +47,13 @@ const TextField: FC = ({
- setValue(e.target.value)} onBlur={onBlur} /> + dispatch(setFieldValue(name, e.target.value))} + onBlur={onBlur} /> {icon && diff --git a/src/components/forms/text-field/index.ts b/src/components/forms/text-field/index.ts deleted file mode 100644 index 6e3c0f9..0000000 --- a/src/components/forms/text-field/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Dispatch } from 'redux' -import { connect } from 'react-redux' - -import { setFieldValue } from 'src/actions/forms' -import { getFieldValue, getFieldNotification } from 'src/selectors/forms' -import { AppState } from 'src/types' - -import TextField, { Props } from './text-field' - -const mapStateToProps = (state: AppState, ownProps: Props) => ({ - value: getFieldValue(state, ownProps.name, ''), - notification: getFieldNotification(state, ownProps.name), -}) - -const mapDispatchToProps = (dispatch: Dispatch, ownProps: Props) => ({ - setValue: (value: string) => { - dispatch(setFieldValue(ownProps.name, value)) - } -}) - -export default connect( - mapStateToProps, - mapDispatchToProps -)(TextField) diff --git a/src/components/forms/textarea-field/textarea-field.tsx b/src/components/forms/textarea-field.tsx similarity index 56% rename from src/components/forms/textarea-field/textarea-field.tsx rename to src/components/forms/textarea-field.tsx index c085735..b3d76bf 100644 --- a/src/components/forms/textarea-field/textarea-field.tsx +++ b/src/components/forms/textarea-field.tsx @@ -1,29 +1,31 @@ import React, { FC, FocusEventHandler } from 'react' +import { useSelector, useDispatch } from 'react-redux' import classNames from 'classnames' import noop from 'lodash/noop' -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { setFieldValue } from 'src/actions/forms' +import { getFieldValue, getFieldNotification } from 'src/selectors/forms' import { notificationTypeToClassName } from 'src/utils' -import { FormNotification, ClassDictionary } from 'src/types' +import { AppState, FormNotification, ClassDictionary } from 'src/types' export interface Props { name: string label: string placeholder?: string - value?: string - notification?: FormNotification - setValue?: (value: string) => void onBlur?: FocusEventHandler } const TextField: FC = ({ + name, label, placeholder, - value = '', - notification, - setValue = noop, onBlur = noop, }) => { + const value = useSelector(state => getFieldValue(state, name, '')) + const notification = useSelector(state => getFieldNotification(state, name)) + + const dispatch = useDispatch() + const helpClassDictionary: ClassDictionary = { help: true } const inputClassDictionary: ClassDictionary = { textarea: true } @@ -38,7 +40,12 @@ const TextField: FC = ({
-