diff --git a/package-lock.json b/package-lock.json index d91426e..702b407 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,8 +8,11 @@ "name": "react-auth-template", "version": "0.1.0", "dependencies": { + "@ramonak/react-progress-bar": "^5.0.3", "classnames": "^2.3.2", + "env-cmd": "^10.1.0", "firebase": "^8.10.1", + "jsonwebtoken": "^9.0.0", "papaparse": "^5.4.1", "react": "^17.0.2", "react-circular-progressbar": "^2.1.0", @@ -18,6 +21,8 @@ "react-scripts": "4.0.3", "react-search-box": "^2.3.0", "react-select": "^5.7.0", + "react-slidy": "^4.3.3", + "react-spinners": "^0.13.8", "react-toastify": "^8.1.0" }, "devDependencies": { @@ -1749,11 +1754,11 @@ } }, "node_modules/@babel/runtime": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", - "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" }, "engines": { "node": ">=6.9.0" @@ -1886,17 +1891,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@emotion/babel-plugin/node_modules/@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@emotion/babel-plugin/node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -1933,11 +1927,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/@emotion/babel-plugin/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, "node_modules/@emotion/babel-plugin/node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -2011,22 +2000,6 @@ } } }, - "node_modules/@emotion/react/node_modules/@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emotion/react/node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, "node_modules/@emotion/serialize": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", @@ -3336,6 +3309,15 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@ramonak/react-progress-bar": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@ramonak/react-progress-bar/-/react-progress-bar-5.0.3.tgz", + "integrity": "sha512-VxXGKN74q94jYoeYuFNJm3xvWhVz9dy+alFZ8S4ZmTTr/05CCq9PjwthT8JB27UdAvn8pHvKBmemV8JU2cZi6A==", + "peerDependencies": { + "react": "^16.0.0 || ^17 || ^18", + "react-dom": "^16.0.0 || ^17 || ^18" + } + }, "node_modules/@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -5737,6 +5719,11 @@ "isarray": "^1.0.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7554,6 +7541,14 @@ "stream-shift": "^1.0.0" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -7676,6 +7671,75 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-cmd": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/env-cmd/-/env-cmd-10.1.0.tgz", + "integrity": "sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==", + "dependencies": { + "commander": "^4.0.0", + "cross-spawn": "^7.0.0" + }, + "bin": { + "env-cmd": "bin/env-cmd.js" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/env-cmd/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/env-cmd/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/env-cmd/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/env-cmd/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/env-cmd/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -10580,6 +10644,11 @@ "node": ">= 0.4" } }, + "node_modules/intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" + }, "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -12791,6 +12860,35 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "dependencies": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/jsx-ast-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", @@ -12803,6 +12901,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -15784,13 +15901,13 @@ } }, "node_modules/prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "node_modules/protobufjs": { @@ -16449,6 +16566,28 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/react-slidy": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/react-slidy/-/react-slidy-4.3.3.tgz", + "integrity": "sha512-SWdHk+x5epue3Kg5YdpO0kPhjh+K4PbOSKvRNbt++KEhG87NbvY6kLmuGQ0CmhzoW26vVMMkT4Conjj0ylW8Tg==", + "dependencies": { + "intersection-observer": "0.10.0" + }, + "peerDependencies": { + "prop-types": "15", + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0" + } + }, + "node_modules/react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-toastify": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.2.0.tgz", @@ -16586,9 +16725,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -19046,9 +19185,9 @@ } }, "node_modules/tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -22526,11 +22665,11 @@ } }, "@babel/runtime": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.0.tgz", - "integrity": "sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.11" } }, "@babel/runtime-corejs3": { @@ -22630,14 +22769,6 @@ "@babel/helper-plugin-utils": "^7.18.6" } }, - "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, "babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -22661,11 +22792,6 @@ "has": "^1.0.3" } }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, "resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -22721,21 +22847,6 @@ "@emotion/utils": "^1.2.0", "@emotion/weak-memoize": "^0.3.0", "hoist-non-react-statics": "^3.3.1" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "requires": { - "regenerator-runtime": "^0.13.11" - } - }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - } } }, "@emotion/serialize": { @@ -23769,6 +23880,12 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "@ramonak/react-progress-bar": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@ramonak/react-progress-bar/-/react-progress-bar-5.0.3.tgz", + "integrity": "sha512-VxXGKN74q94jYoeYuFNJm3xvWhVz9dy+alFZ8S4ZmTTr/05CCq9PjwthT8JB27UdAvn8pHvKBmemV8JU2cZi6A==", + "requires": {} + }, "@remix-run/router": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.0.3.tgz", @@ -25677,6 +25794,11 @@ } } }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -27131,6 +27253,14 @@ "stream-shift": "^1.0.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -27229,6 +27359,53 @@ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, + "env-cmd": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/env-cmd/-/env-cmd-10.1.0.tgz", + "integrity": "sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==", + "requires": { + "commander": "^4.0.0", + "cross-spawn": "^7.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "errno": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz", @@ -29437,6 +29614,11 @@ "side-channel": "^1.0.4" } }, + "intersection-observer": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/intersection-observer/-/intersection-observer-0.10.0.tgz", + "integrity": "sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==" + }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", @@ -31068,6 +31250,27 @@ "universalify": "^2.0.0" } }, + "jsonwebtoken": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz", + "integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==", + "requires": { + "jws": "^3.2.2", + "lodash": "^4.17.21", + "ms": "^2.1.1", + "semver": "^7.3.8" + }, + "dependencies": { + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "jsx-ast-utils": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz", @@ -31077,6 +31280,25 @@ "object.assign": "^4.1.2" } }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -33466,13 +33688,13 @@ } }, "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^16.13.1" } }, "protobufjs": { @@ -33993,6 +34215,20 @@ "use-isomorphic-layout-effect": "^1.1.2" } }, + "react-slidy": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/react-slidy/-/react-slidy-4.3.3.tgz", + "integrity": "sha512-SWdHk+x5epue3Kg5YdpO0kPhjh+K4PbOSKvRNbt++KEhG87NbvY6kLmuGQ0CmhzoW26vVMMkT4Conjj0ylW8Tg==", + "requires": { + "intersection-observer": "0.10.0" + } + }, + "react-spinners": { + "version": "0.13.8", + "resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.13.8.tgz", + "integrity": "sha512-3e+k56lUkPj0vb5NDXPVFAOkPC//XyhKPJjvcGjyMNPWsBKpplfeyialP74G7H7+It7KzhtET+MvGqbKgAqpZA==", + "requires": {} + }, "react-toastify": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-8.2.0.tgz", @@ -34104,9 +34340,9 @@ } }, "regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" }, "regenerator-transform": { "version": "0.14.5", @@ -36033,9 +36269,9 @@ } }, "tslib": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", - "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "tsutils": { "version": "3.21.0", diff --git a/package.json b/package.json index a6f4ed8..3c21a4b 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,11 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ramonak/react-progress-bar": "^5.0.3", "classnames": "^2.3.2", + "env-cmd": "^10.1.0", "firebase": "^8.10.1", + "jsonwebtoken": "^9.0.0", "papaparse": "^5.4.1", "react": "^17.0.2", "react-circular-progressbar": "^2.1.0", @@ -13,12 +16,14 @@ "react-scripts": "4.0.3", "react-search-box": "^2.3.0", "react-select": "^5.7.0", + "react-slidy": "^4.3.3", + "react-spinners": "^0.13.8", "react-toastify": "^8.1.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "react-scripts", + "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { diff --git a/src/App.js b/src/App.js index dbc70aa..929b4b8 100644 --- a/src/App.js +++ b/src/App.js @@ -31,15 +31,18 @@ const App = () => { }> } /> } /> - } /> - } /> - } /> + } /> + } + /> + } /> } /> } /> } /> } /> - } /> + } /> diff --git a/src/components/AddChild/index.js b/src/components/AddChild/index.js new file mode 100644 index 0000000..031c506 --- /dev/null +++ b/src/components/AddChild/index.js @@ -0,0 +1,146 @@ +import React, { useState, useEffect } from "react"; +import classNames from "classnames/bind"; +import styles from "./index.module.css"; +import { useWindowSize } from "../../lib/hooks"; +import { + AUTH_INPUT_LABELS, + SCHOOL_DISTRICT, + DISABILITY, + WINDOW_TYPE, + STATUS_CODE, +} from "../../lib/constants"; +import { AuthInputBlock } from "../../components/AuthInputBlock"; +import { AuthSelectBlock } from "../../components/AuthSelectBlock"; +import { AuthButton } from "../AuthButton"; + +const cx = classNames.bind(styles); + +export function AddChild(props) { + const isMobile = useWindowSize().type === WINDOW_TYPE.MOBILE; + + const [child, setChild] = useState({}); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(""); + const [submitted, setSubmitted] = useState(false); + const [leavePage, setLeavePage] = useState(false); + + useEffect(() => { + if (leavePage) { + if (submitted) { + props.setChildren([...props.currChildren, child]); + } + props.setAddChild(false); + } + }, [leavePage]); + + const checkValidBirthday = () => { + const birthday = child.birthDate; + + // Regular expression for MM-DD-YYYY format + const regex = /^\d{2}-\d{2}-\d{4}$/; + + // Check if the input matches the regular expression + if (!regex.test(birthday)) { + return false; + } + + // Check if the date is valid + const parts = birthday.split("-"); + const month = parseInt(parts[0], 10); + const day = parseInt(parts[1], 10); + const year = parseInt(parts[2], 10); + + // JavaScript Date object will automatically handle leap year + const date = new Date(year, month - 1, day); + const isValid = + date.getFullYear() === year && + date.getMonth() === month - 1 && + date.getDate() === day; + + return isValid; + }; + + const onSubmit = async () => { + if (!child.firstName) props.toast("Please provide your child's name"); + else if (!child.birthDate) + props.toast("Please provide your child's birthdate"); + else if (!checkValidBirthday(child.birthDate)) + props.toast("Please enter the birthday in the correct format"); + else if (!child.schoolDistrict) + props.toast("Please provide your child's school district"); + else if (!child.disabilities) + props.toast("Please provide your child's disability"); + else { + setSubmitted(true); + setLeavePage(true); + } + }; + + return ( +
+
+
+
+ setLeavePage(true)} + style={{ + borderRadius: "30%", + height: "auto", + width: "30px", + display: "flex", + alignItems: "center", + justifyContent: "center", + }} + /> +
{" "} +
+

Add a Child

+ setChild({ ...child, firstName: value })} + /> + setChild({ ...child, birthDate: value })} + /> + + setChild({ ...child, schoolDistrict: value.label }) + } + /> + setChild({ ...child, disabilities: value })} + /> + +
+
+ ); +} diff --git a/src/components/AddChild/index.module.css b/src/components/AddChild/index.module.css new file mode 100644 index 0000000..f9175f3 --- /dev/null +++ b/src/components/AddChild/index.module.css @@ -0,0 +1,85 @@ +.content { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100vh; + background-color: rgba(0, 0, 0, 0.2); + + display: flex; + justify-content: center; + align-items: center; +} + +.popup { + display: block; + background-color: white; + width: 80%; + height: auto; + + font-family: "Poppins"; + padding: 30px; + border-radius: 30px; +} + +.title { + margin: 0; + font-size: 7vw; +} + +.desc { + font-size: 4vw; + width: 100%; +} + +.input { + width: 95%; + font-family: "Poppins"; + font-size: 3vw; + height: 30vh; + padding: 10px; + margin: 15px 0; + border-radius: 20px; +} + +.submit { + /* background-color: rgb(2, 152, 186); + border-radius: 20px; + font-size: 5vw; + padding: 10px; + width: 50%; + color: white; */ + width: 99%; + height: 6vh; + font-size: 2vh; + font-family: "Poppins"; + font-weight: bold; + color: white; + border-width: 0; + border-radius: 20px; + background-color: rgb(2, 152, 186); + padding: 0; +} +.submit:hover { + cursor: pointer; + transform: scale(1.01); + transition: 0.2s; + box-shadow: 0 6px 4px darkgray; +} + +.submit_wrapper { + display: flex; + align-items: center; + justify-content: center; + border-width: 0; +} + +.close_wrapper { + display: flex; + border-radius: 50%; +} + +.close { + margin-left: auto; + width: 5vw; +} diff --git a/src/components/AllTasksSection/index.js b/src/components/AllTasksSection/index.js index 664a9c1..6cacc09 100644 --- a/src/components/AllTasksSection/index.js +++ b/src/components/AllTasksSection/index.js @@ -2,27 +2,55 @@ import styles from "./index.module.css"; import classNames from "classnames/bind"; import React from "react"; import { TaskListItem } from "../../components/TaskListItem"; +import { useState, useEffect } from "react"; +import { COLORS_ARR } from "../../lib/constants"; const cx = classNames.bind(styles); export const AllTasksSection = React.forwardRef((props, ref) => { - const { taskList } = props; + const { taskList, isMobile } = props; + const [isExpanded, setIsExpanded] = useState(false); + const [color, setColor] = useState("#0198BA26"); + + const toggleExpansion = () => { + setIsExpanded(!isExpanded); + }; + + useEffect(() => { + // randomize color for the display block + setColor(COLORS_ARR[Math.floor(Math.random() * COLORS_ARR.length)]); + }, []); return ( -
-

{taskList[0]?.childName}

-
- {taskList.map((item, index) => ( - - ))} +
+

+ {taskList[0]?.childName} +

+
+ {isExpanded && + taskList.map((item, index) => ( + + ))}
); diff --git a/src/components/AllTasksSection/index.module.css b/src/components/AllTasksSection/index.module.css index e8e3ad7..cd86ac8 100644 --- a/src/components/AllTasksSection/index.module.css +++ b/src/components/AllTasksSection/index.module.css @@ -1,17 +1,40 @@ @import url("https://fonts.googleapis.com/css?family=Poppins"); -.tasksSection { - border-bottom: 3px solid black; - margin-left: 3vw; - margin-right: 3vw; - padding: 4vw; +.entireSection { + border-radius: 50px; + margin-top: 2vw; + margin-bottom: 1vw; + padding-top: 1vw; + padding-bottom: 1vw; + } + + .entireSection.mobile { + border-radius: 50px; + margin-top: 3vw; + margin-bottom: 3vw; + padding-top: 3vw; + padding-bottom: 3vw; } .childName { font-family: "Poppins"; font-weight:400; - font-size: 3vw; - color: #8B5674; + font-size: 1.5vw; + margin: 0; + margin-left: 6vh; +} + +.childName.mobile { + font-family: "Poppins"; + font-weight:400; + font-size: 5vw; margin: 0; - margin-right: auto; + margin-left: 6vh; +} + + +.tasksSection { + margin-top: 3vw; + margin-bottom: 3vw; + background-color: rgba(255, 255, 255, 0.5); } \ No newline at end of file diff --git a/src/components/AuthButton/index.module.css b/src/components/AuthButton/index.module.css index 93030ab..58aaa5b 100644 --- a/src/components/AuthButton/index.module.css +++ b/src/components/AuthButton/index.module.css @@ -2,22 +2,23 @@ @value font from "../../index.module.css"; .button { - width: 100%; - height: 55px; + width: 50%; + height: 5vh; background-color: #0198BA; color: white; font-family: "Poppins"; - font-size: 20px; + font-size: 2vw; display: flex; justify-content: center; align-items: center; border-radius: 5px; cursor: pointer; + margin: auto; } .button.mobile { - width: 100%; - min-height: 7vh; + width: 80%; + min-height: 5vh; background-color: #0198BA; color: white; font-family: "Poppins"; @@ -25,6 +26,7 @@ display: flex; justify-content: center; align-items: center; - border-radius: 20px; + border-radius: 2vh; cursor: pointer; + margin: 4vh; } \ No newline at end of file diff --git a/src/components/AuthInputBlock/index.js b/src/components/AuthInputBlock/index.js index bd22279..47e1806 100644 --- a/src/components/AuthInputBlock/index.js +++ b/src/components/AuthInputBlock/index.js @@ -15,6 +15,7 @@ export const AuthInputBlock = (props) => { isMobile } = props; + console.log(isMobile) return
{ +export const BackArrow = ({ showBackArrow = true, isMobile }) => { const navigate = useNavigate(); const goBack = () => { navigate(-1); @@ -16,14 +16,13 @@ export const BackArrow = ({ showBackArrow = true }) => { <> {showBackArrow && ( - //
- - //
)} ); diff --git a/src/components/BackArrow/index.module.css b/src/components/BackArrow/index.module.css index b9bc152..216eec4 100644 --- a/src/components/BackArrow/index.module.css +++ b/src/components/BackArrow/index.module.css @@ -1,7 +1,16 @@ .backArrow { display: flex; justify-content: center; - width: 5%; + width: 2%; + height: auto; + margin-top: 4vh; + margin-left: 5%; + } + + .backArrow.mobile { + display: flex; + justify-content: center; + width: 4%; height: auto; margin-top: 4vh; margin-left: 5%; diff --git a/src/components/Caption/index.js b/src/components/Caption/index.js index d7cd1d2..7bedd4f 100644 --- a/src/components/Caption/index.js +++ b/src/components/Caption/index.js @@ -1,45 +1,46 @@ -import { useWindowSize } from '../../lib/hooks'; -import styles from './index.module.css'; -import classNames from 'classnames/bind'; -import React from 'react'; -import { WINDOW_TYPE } from '../../lib/constants'; -import PropTypes from 'prop-types'; +import { useWindowSize } from "../../lib/hooks"; +import styles from "./index.module.css"; +import classNames from "classnames/bind"; +import React from "react"; +import { WINDOW_TYPE } from "../../lib/constants"; +import PropTypes from "prop-types"; const cx = classNames.bind(styles); // The caption of each home page export const Caption = (props) => { - const { - mainTitle, - subTitle, - className, - style, - } = props; + const { mainTitle, subTitle, className, style } = props; - const { type } = useWindowSize(); - const isMobile = type === WINDOW_TYPE.MOBILE; - - return
-
- {mainTitle} -
-
- {subTitle} -
+
+ {mainTitle} +
+
+ {subTitle} +
+ ); }; Caption.propTypes = { - mainTitle: PropTypes.string.isRequired, - subTitle: PropTypes.string.isRequired, - className: PropTypes.string, - style: PropTypes.objectOf(PropTypes.string), -} \ No newline at end of file + mainTitle: PropTypes.string.isRequired, + subTitle: PropTypes.string.isRequired, + className: PropTypes.string, + style: PropTypes.objectOf(PropTypes.string), +}; diff --git a/src/components/Caption/index.module.css b/src/components/Caption/index.module.css index 75dfc26..403d0c2 100644 --- a/src/components/Caption/index.module.css +++ b/src/components/Caption/index.module.css @@ -9,36 +9,36 @@ border: 0; } -.mainTitle.mobile { +.mainTitle { height: fit-content; font-family: "Poppins"; font-style: normal; - font-size: 8vw; + font-size: 3vw; font-weight: 900; } -.mainTitle { +.mainTitle.mobile { height: fit-content; font-family: "Poppins"; font-style: normal; - font-size: 4vw; + font-size: 8vw; font-weight: 900; } -.subTitle.mobile { +.subTitle { height: fit-content; font-family: "Poppins"; font-style: normal; - font-size: 4vw; + font-size: 1vw; font-weight: 500; color: #818181; } -.subTitle { +.subTitle.mobile { height: fit-content; font-family: "Poppins"; font-style: normal; - font-size: 2vw; + font-size: 4vw; font-weight: 500; color: #818181; } \ No newline at end of file diff --git a/src/components/CheckBox/index.js b/src/components/CheckBox/index.js index 7351eaa..dd81649 100644 --- a/src/components/CheckBox/index.js +++ b/src/components/CheckBox/index.js @@ -7,14 +7,13 @@ const cx = classNames.bind(styles); // The caption of each home page export const CheckBox = (props) => { - const { value, onChange } = props; - - const { type } = useWindowSize(); - const isMobile = type === WINDOW_TYPE.MOBILE; + const { value, onChange, isMobile } = props; return ( {
- +

- {props.user} + {props.name}

-

{props.location}

+ {props.heading}
-

{props.content}

+

{props.body}

-
+ {/*
- {props.user}  + {props.name}  - {props.caption} -
-
-

- View all {props.comments.length} comments -

- {props.comments[0].user}  + {props.comments}  - {props.comments[0].comment} + {props.comments}

-
+
*/}
); }; diff --git a/src/components/CommunityPost/index.module.css b/src/components/CommunityPost/index.module.css index 4e18e82..b265e64 100644 --- a/src/components/CommunityPost/index.module.css +++ b/src/components/CommunityPost/index.module.css @@ -39,7 +39,7 @@ } .post_info { - font-size: 1.5vw; + font-size: 3.5vw; } .post_caption { diff --git a/src/components/CreatePost/index.js b/src/components/CreatePost/index.js index f48d999..f49848b 100644 --- a/src/components/CreatePost/index.js +++ b/src/components/CreatePost/index.js @@ -1,12 +1,14 @@ import React, { useState } from "react"; import classNames from "classnames/bind"; import styles from "./index.module.css"; +import { AuthInputBlock } from "../AuthInputBlock"; const cx = classNames.bind(styles); -export function CreatePost() { +export function CreatePost(props) { const [postContent, setPostContent] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [postTitle, setPostTitle] = useState(""); const [error, setError] = useState(""); const handleSubmit = (e) => { @@ -14,12 +16,16 @@ export function CreatePost() { setIsLoading(true); setError(""); - fetch("/createPost", { + fetch(process.env.REACT_APP_HOST_URL + "/posts", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ postContent }), + body: JSON.stringify({ + name: localStorage.getItem("firstName"), + title: postTitle, + body: postContent, + }), }) .then((res) => { if (!res.ok) { @@ -35,6 +41,8 @@ export function CreatePost() { setIsLoading(false); setError(err.message); }); + props.setCreatePost(false); + props.rotate(false); }; return ( @@ -42,6 +50,14 @@ export function CreatePost() {

Create a Post

+
+ setPostTitle(value)} + /> +
+
); -} +} \ No newline at end of file diff --git a/src/components/CreatePost/index.module.css b/src/components/CreatePost/index.module.css index b66a01c..f0d6f48 100644 --- a/src/components/CreatePost/index.module.css +++ b/src/components/CreatePost/index.module.css @@ -5,7 +5,6 @@ width: 100%; height: 100vh; background-color: rgba(0, 0, 0, 0.2); - display: flex; justify-content: center; align-items: center; @@ -15,7 +14,7 @@ display: block; background-color: white; width: 80%; - height: 50%; + height: auto; font-family: "Poppins"; padding: 30px; @@ -73,3 +72,7 @@ justify-content: center; border-width: 0; } + +.content_container { + font-size: 10vw; +} \ No newline at end of file diff --git a/src/components/OnYourRadar/index.js b/src/components/OnYourRadar/index.js new file mode 100644 index 0000000..90adad5 --- /dev/null +++ b/src/components/OnYourRadar/index.js @@ -0,0 +1,97 @@ +import styles from "./index.module.css"; +import classNames from "classnames/bind"; +import React from "react"; +import { useState, useEffect } from "react"; +import { getChildrenTasksArray } from "../../lib/services"; +import { Loader } from "../../pages/LoadScreen"; + +const cx = classNames.bind(styles); + +export const OnYourRadar = React.forwardRef((props, ref) => { + const { childrenId, isMobile } = props; + const [hpList, sethpList] = useState([]); + const [elseList, setElseList] = useState([]); + + useEffect(async () => { + const { taskArray } = await getChildrenTasksArray(childrenId, true); + sethpList(taskArray); + }, []); + + useEffect(async () => { + const { taskArray } = await getChildrenTasksArray(childrenId, false); + setElseList(taskArray); + }, []); + + const hpElements = hpList.flat().map((thing, index) => ( +

+ {index + 1 + ". " + thing.title} +

+ )); + const elseElements = elseList.flat().map((thing, index) => ( +

+ {index + 1 + ". " + thing.title} +

+ )); + + return ( +
+

+ On Your Radar +

+
+
+

+ High Priority +

+

+ {hpElements} +

+
+
+

+ All Tasks +

+

+ {elseElements} +

+
+
+
+ ); +}); diff --git a/src/components/OnYourRadar/index.module.css b/src/components/OnYourRadar/index.module.css new file mode 100644 index 0000000..98cf67b --- /dev/null +++ b/src/components/OnYourRadar/index.module.css @@ -0,0 +1,85 @@ +.todo_div { + border: 3px #0198ba; + border-style: solid; + border-radius: 50px; + margin-top: 5vw; + margin-left: 2vw; + margin-right: 2vw; + padding-left: 3vw; + padding-bottom: 10vw; +} + +.todo_div.mobile { + border: 6px #0198ba; + border-style: solid; + border-radius: 50px; + margin-top: 10vw; + margin-left: 2vw; + margin-right: 2vw; + padding-left: 3vw; + padding-bottom: 10vw; +} + +.priorityParent{ + display: flex; + justify-content: space-between; +} +.priorityBlock{ + flex: 1; + display: inline-block; +} + +.priorityParent.mobile{ + display: block; +} + +.priorityBlock.mobile{ + display: block; +} + +.radar.mobile { + font-family: "Poppins"; + font-weight: 900; + color: black; + font-size: 3vw; + margin-top: 3vw; +} + +.radar { + font-family: "Poppins"; + font-weight: 900; + color: black; + font-size: 2vw; +} + +.priority.mobile { + font-family: "Poppins"; + font-weight: bolder; + color: #8b5674; + font-size: 3vw; +} + +.priority { + font-family: "Poppins"; + font-weight: bolder; + color: #8b5674; + font-size: 2vw; +} + +.priority.else { + color: #e3d150; +} + +.list { + font-family: 'Poppins'; + color: black; + font-weight: 600; + font-size: 1.7vw; +} + +.list.mobile { + font-family: 'Poppins'; + color: black; + font-weight: 600; + font-size: 6vw; +} \ No newline at end of file diff --git a/src/components/PointsDisplay/index.js b/src/components/PointsDisplay/index.js new file mode 100644 index 0000000..3fdd6c3 --- /dev/null +++ b/src/components/PointsDisplay/index.js @@ -0,0 +1,70 @@ +import styles from "./index.module.css"; +import classNames from "classnames/bind"; +import React from "react"; +import { + CircularProgressbarWithChildren, + buildStyles, +} from "react-circular-progressbar"; + +const cx = classNames.bind(styles); + +export const PointsDisplay = React.forwardRef((props, ref) => { + const { childName, points, goal, isMobile } = props; + + return ( +
+
+

+ {childName} +

+

+ {goal - points} +

+

+  points away from weekly goal +

+
+
+ +
+ {points} +
+
+ Points +
+
+
+
+ ); +}); diff --git a/src/components/PointsDisplay/index.module.css b/src/components/PointsDisplay/index.module.css new file mode 100644 index 0000000..ae6dfd2 --- /dev/null +++ b/src/components/PointsDisplay/index.module.css @@ -0,0 +1,82 @@ +.pointsBlock { + margin-top: 4vw +} + +.text_div { + margin-left: auto; + margin-right: auto; + text-align: center; + position: relative; +} + +.name { + font-family: 'Poppins'; + font-weight: 800; + font-size: 2vw; + margin: auto; + color: #000000; +} + +.name.mobile { + font-family: 'Poppins'; + font-weight: 800; + font-size: 5vw; + margin: 1vh; + color: #000000; +} + + +.points.mobile { + font-family: 'Poppins'; + font-weight: 800; + font-size: 4.2vw; + margin: auto; + display: inline-block; + color: #000000; +} + +.points { + font-family: 'Poppins'; + font-weight: 800; + font-size: 1.8vw; + margin: auto; + display: inline-block; + color: #000000; +} + +.points.text { + font-weight: 600; + color: #818181; +} + +.progress_circle { + width: 20vw; + height: 20vw; + margin-top: 4vw; + margin-bottom: 2vw; + margin-left: auto; + margin-right: auto; +} + +.progress_circle.mobile { + width: 40vw; + height: 40vw; + margin-top: 5vw; + margin-bottom: 5vw; + margin-left: auto; + margin-right: auto; +} + +.progress_circle_text { + font-family: 'Poppins'; + font-weight: 900; + color: black; + font-size: 2vw; +} + +.progress_circle_text.mobile { + font-family: 'Poppins'; + font-weight: 900; + color: black; + font-size: 6vw; +} \ No newline at end of file diff --git a/src/components/ProfilePicture/index.js b/src/components/ProfilePicture/index.js index edee73e..63f1ef8 100644 --- a/src/components/ProfilePicture/index.js +++ b/src/components/ProfilePicture/index.js @@ -1,13 +1,14 @@ import React from "react"; import styles from "./index.module.css"; import classNames from "classnames"; +import PFP from "../../images/PFP.png"; const cx = classNames.bind(styles); -function ProfilePicture(props) { +function ProfilePicture() { return (
- +
); } diff --git a/src/components/Slider/index.js b/src/components/Slider/index.js new file mode 100644 index 0000000..48bf209 --- /dev/null +++ b/src/components/Slider/index.js @@ -0,0 +1,29 @@ +import React from "react"; +import styles from "./index.module.css"; +import classNames from "classnames/bind"; +import { PointsDisplay } from "../PointsDisplay"; +import ReactSlidy from "react-slidy"; +import "react-slidy/lib/styles.css"; +import { getRandomColor } from "../../lib/utils"; + +const cx = classNames.bind(styles); + +// build a page slider component +export const Slider = (props) => { + const { childrenStats, isMobile } = props; + + return ( + + {childrenStats.map((childStats) => ( + + ))} + + ); +}; diff --git a/src/components/Slider/index.module.css b/src/components/Slider/index.module.css new file mode 100644 index 0000000..e69de29 diff --git a/src/components/TaskListItem/index.js b/src/components/TaskListItem/index.js index cc9a58c..b89cdb8 100644 --- a/src/components/TaskListItem/index.js +++ b/src/components/TaskListItem/index.js @@ -10,7 +10,7 @@ import { checkEvent, uncheckEvent } from "../../lib/services"; const cx = classNames.bind(styles); export const TaskListItem = React.forwardRef((props, ref) => { - const { taskName, dueAt, taskId, childName, childId, completed } = props; + const { taskName, dueAt, taskId, childId, completed, isMobile } = props; const navigate = useNavigate(); const [checked, setChecked] = useState(completed); @@ -29,11 +29,22 @@ export const TaskListItem = React.forwardRef((props, ref) => { }) } > -

{taskName}

-

{dueAt}

-

{childName}

+

+ {taskName} +

+

+ {dueAt} +

- +
); }); diff --git a/src/components/TaskListItem/index.module.css b/src/components/TaskListItem/index.module.css index 833737e..2e672f8 100644 --- a/src/components/TaskListItem/index.module.css +++ b/src/components/TaskListItem/index.module.css @@ -1,15 +1,28 @@ @import url("https://fonts.googleapis.com/css?family=Poppins"); .tasks_div { + border-bottom: 1px solid black; + padding: 2vw; + display: flex; + justify-content: flex-end; + } + +.tasks_div.mobile { border-bottom: 3px solid black; - margin-left: 3vw; - margin-right: 3vw; padding: 4vw; display: flex; justify-content: flex-end; } .taskName { + font-family: "Poppins"; + font-style: normal; + font-size: 1.4vw; + font-weight: 500; + margin: 0; +} + +.taskName.mobile { font-family: "Poppins"; font-style: normal; font-size: 5vw; @@ -17,7 +30,7 @@ margin: 0; } -.taskDate { +.taskDate.mobile { font-family: "Poppins"; font-weight:400; font-size: 4vw; @@ -25,11 +38,10 @@ margin: 0; } -.childName { +.taskDate { font-family: "Poppins"; font-weight:400; - font-size: 3vw; + font-size: 1.2vw; color: #8B5674; margin: 0; - margin-right: auto; } \ No newline at end of file diff --git a/src/components/UpcomingComponent/index.js b/src/components/UpcomingComponent/index.js index 0280ae2..6f8dc08 100644 --- a/src/components/UpcomingComponent/index.js +++ b/src/components/UpcomingComponent/index.js @@ -5,6 +5,7 @@ import { useNavigate } from "react-router-dom"; import { ROUTES } from "../../lib/constants"; import { CheckBox } from "../CheckBox"; import { checkEvent, uncheckEvent } from "../../lib/services"; +import { COLORS_ARR } from "../../lib/constants"; import React from "react"; const cx = classNames.bind(styles); @@ -18,6 +19,7 @@ export const UpcomingComponent = (props) => { completed, priority, childId, + childName, isMobile, } = props; const [checked, setChecked] = useState(completed); @@ -25,8 +27,7 @@ export const UpcomingComponent = (props) => { useEffect(() => { // randomize color for the display block - const colors = ["#0198BA26", "#E3D15033", "#8B567478"]; - setColor(colors[Math.floor(Math.random() * colors.length)]); + setColor(COLORS_ARR[Math.floor(Math.random() * COLORS_ARR.length)]); }, []); const handleChecked = () => { @@ -41,6 +42,7 @@ export const UpcomingComponent = (props) => { className={cx(styles.upcomingWrapper)} style={{ backgroundColor: color, + padding: "4vh", }} >
{ className={cx(styles.title, { [styles.mobile]: isMobile, })} + onClick={() => + navigate(ROUTES.TASK_DETAILS, { + state: { taskId, completed, childId }, + }) + } > - {title} + {title + " - " + childName}

- {/* */}

{ }) } > - {time} + {time + ", priority level: " + priority}

{ > {content}

-

- navigate(ROUTES.TASK_DETAILS, { state: { taskId, completed } }) - } - > - {priority} -

); diff --git a/src/components/UpcomingComponent/index.module.css b/src/components/UpcomingComponent/index.module.css index ed628d7..9148835 100644 --- a/src/components/UpcomingComponent/index.module.css +++ b/src/components/UpcomingComponent/index.module.css @@ -2,8 +2,6 @@ .upcomingWrapper { composes: font; - margin: 4vw 0; - padding: 4vw; background-color: rgba(1, 152, 186, 0.15); border-radius: 50px; } @@ -12,50 +10,34 @@ display: flex; } -.header.mobile { - display: flex; -} - .title{ font-style: normal; - font-size: 4vw; + font-size: 1.7vw; font-weight: 500; margin: 0; color: #000000; + width: 90%; } .title.mobile { - font-style: normal; font-size: 7vw; - font-weight: 500; - margin: 0; - color: #000000; } .time{ font-style: normal; font-weight: 400; - font-size: 1.5vw; - color: #8B5674; - margin: 2; - + font-size: 1.2vw; color: #0198BA; } .time.mobile{ - font-style: normal; - font-weight: 400; font-size: 3vw; - color: #8B5674; - margin: 2; - - color: #0198BA; } .content{ font-style: normal; font-weight: 300; - font-size: 2vw; + font-size: 1.2vw; color: #8B5674; margin: 2; @@ -63,17 +45,5 @@ } .content.mobile { - font-style: normal; - font-weight: 300; font-size: 3vw; - color: #8B5674; - margin: 2; - - color: #818181; -} - -.upcomingIcon { - width: 20px; - height: 20px; - cursor: pointer; } \ No newline at end of file diff --git a/src/lib/constants.js b/src/lib/constants.js index 00a9960..e9ce7b4 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -3,6 +3,8 @@ export const PRIORITY_LEVEL = { PRIORITY_LEVEL: 2, }; +export const COLORS_ARR = ["#0198BA26", "#E3D15033", "#8B567478"]; + export const WINDOW_TYPE = { WEB: "WEB", MOBILE: "MOBILE", @@ -10,7 +12,7 @@ export const WINDOW_TYPE = { export const STATUS_CODE = { SUCESS: "SUCCESS", - ERROR: "FAILURE", + ERROR: "ERROR", }; export const ROUTES = { @@ -25,7 +27,7 @@ export const ROUTES = { UPCOMING_TASKS: "/upcoming", ALL_TASKS: "/tasks", TASK_DETAILS: "/task-details", - VERIFICATION: "/verifyEmail", + VERIFICATION: "/verification", }; export const HOME_NAV_LABELS = { @@ -47,6 +49,8 @@ export const AUTH_INPUT_LABELS = { ZIP_CODE: "Zip Code", PHONE_NUMBER: "Phone Number", SIGN_UP: "Sign Up!", + ADD_CHILD: "Add Child", + BIRTH_DATE: "Birth Date (MM-DD-YYYY)", }; // page titles @@ -140,3 +144,5 @@ export const SCHOOL_DISTRICT = [ { label: "Tomball" }, { label: "Waller" }, ]; + +export const TIMEOUT = 7200000; diff --git a/src/lib/hooks.js b/src/lib/hooks.js index 9866c1d..71e94a3 100644 --- a/src/lib/hooks.js +++ b/src/lib/hooks.js @@ -80,6 +80,11 @@ export const useCaption = () => { */ export const useBackArrow = () => { const { pathname } = useLocation(); + const { width, type } = useWindowSize(); + const isMobile = type === WINDOW_TYPE.MOBILE; + if (!isMobile) { + return { showBackArrow: false }; + } switch (pathname) { case ROUTES.LOGIN: return { showBackArrow: false }; diff --git a/src/lib/icons.js b/src/lib/icons.js index 48d7f57..f25054b 100644 --- a/src/lib/icons.js +++ b/src/lib/icons.js @@ -1,17 +1,13 @@ -import { useWindowSize } from "./hooks"; - export const HomeIcon = (props) => { - const color = props.filled ? "#0198BA" : "#CCCCCC"; - const textColor = props.filled ? "black" : "#CCCCCC"; - const { height } = useWindowSize(); + const { isMobile, filled } = props; + const color = filled ? "#0198BA" : "#CCCCCC"; + const textColor = filled ? "black" : "#CCCCCC"; return ( { }; export const RoadmapIcon = (props) => { - const color = props.filled ? "#0198BA" : "#CCCCCC"; - const textColor = props.filled ? "black" : "#CCCCCC"; - const { height } = useWindowSize(); + const { isMobile, filled } = props; + const color = filled ? "#0198BA" : "#CCCCCC"; + const textColor = filled ? "black" : "#CCCCCC"; return ( { }; export const CommunityIcon = (props) => { - const color = props.filled ? "#0198BA" : "#CCCCCC"; - const textColor = props.filled ? "black" : "#CCCCCC"; - const { height } = useWindowSize(); + const { isMobile, filled } = props; + const color = filled ? "#0198BA" : "#CCCCCC"; + const textColor = filled ? "black" : "#CCCCCC"; return ( { .catch((err) => reject(err)); }); }; - -export const salesForceCheck = (email) => { +export const sendVerificationEmail = (email) => { return new Promise((resolve, reject) => { - //make localhost call a constant - fetch(`http://localhost:3001/verification/checkSF/?email=${email}`, { + fetch(`http://localhost:3001/verification/sendEmail/?email=${email}`, { method: "GET", headers: { "Content-Type": "application/json" }, }) @@ -36,11 +34,12 @@ export const salesForceCheck = (email) => { }); }; -export const sendVerificationEmail = (email) => { +export const signUpChildren = (inputs) => { return new Promise((resolve, reject) => { - fetch(`http://localhost:3001/verification/sendEmail/?email=${email}`, { - method: "GET", + fetch(process.env.REACT_APP_HOST_URL + "/children", { + method: "POST", headers: { "Content-Type": "application/json" }, + body: JSON.stringify(inputs), }) .then((response) => response.json()) .then((response) => resolve(response)) @@ -49,54 +48,22 @@ export const sendVerificationEmail = (email) => { }; export const signUp = (inputs) => { - // need to fix process.env later - // fetch(process.env.HOST_URL + '/users', { - // return mongoCheck(inputs.email).then((res) => { - // console.log(res); - // if (res.status === "Found") { - // console.log("here"); - // return { message: "toLogin" }; - // } else { - // return salesForceCheck(inputs.email).then((res) => { - // console.log(res) - // if (res === STATUS_CODE.SUCESS) { - // return { message: "sendVerification" }; - // } else { - // return { message: "sendSFForm" }; - // } - // }); - // } - // }); - - inputs.schoolDistrict = inputs.schoolDistrict.label; - return fetch("http://localhost:3001" + "/users", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(inputs), - }) - .then((res) => { - console.log(res); - console.log(JSON.stringify(inputs)); + console.log("user call to backend"); + console.log(inputs); + return new Promise((resolve, reject) => { + console.log(JSON.stringify(inputs)); + console.log(inputs); + fetch(process.env.REACT_APP_HOST_URL + "/users", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(inputs), }) - .catch((err) => { - console.log(err); - }); + .then((response) => response.json()) + .then((response) => resolve(response)) + .catch((err) => reject(err)); + }); }; -// export const checkEvent = (inputs) => { -// return new Promise((resolve, reject) => { -// // need to fix process.env later -// fetch("http://localhost:3001/tasks/checkEvent", { -// method: "POST", -// headers: { "Content-Type": "application/json" }, -// body: JSON.stringify(inputs), -// }) -// .then((response) => response.json()) -// .then((response) => resolve(response)) -// .catch((err) => reject(err)); -// }); -// }; - export const getChildrenByIdBatch = (childrenId) => { const url = formGetRequest("/children/getChildrenByIdBatch/", { id: JSON.stringify(childrenId), @@ -163,50 +130,51 @@ export const uncheckEvent = (childId, taskId) => { /** * @param {Array} childrenId an array of children Id's * @param {boolean} priority the priority level queried for Upcoming tasks - * @param {Array} taskArray the task array to be set: a 2D array where each child possesses their respective - * tasks based on age and disabilities - * @param {Function} setTaskArray the function to set task array + * @return {Array} taskArray the nested 2D array of tasks belonging to each child */ -export const getChildrenTasksArray = ( - childrenId, - priority, - taskArray, - setTaskArray -) => { - childrenId.forEach((childId) => { + +export const getChildrenTasksArray = async (childrenId, priority) => { + let taskArray = []; + const childPromises = childrenId.map(async (childId) => { // get child's name and disabilities const childUrl = "/children/" + childId; - fetch(process.env.REACT_APP_HOST_URL + childUrl) - .then((response) => response.json()) - .then((childrenData) => { - const childName = childrenData.firstName; - const age = getAgeGivenBirthday(childrenData.birthDate); - const completedTasks = childrenData.completedTasks; - const params = { - disabilities: JSON.stringify(childrenData.disabilities), - age: JSON.stringify(age), - }; - if (priority) { - params.priority = JSON.stringify(2); - } + const response = await fetch(process.env.REACT_APP_HOST_URL + childUrl); + const childData = await response.json(); + const childName = childData.firstName; + const completedTasks = childData.completedTasks; + const age = getAgeGivenBirthday(childData.birthDate); + const params = { + disabilities: JSON.stringify(childData.disabilities), + age: JSON.stringify(age), + }; + if (priority) { + params.priority = JSON.stringify(2); + } - // get tasks based on children's attributes - const url = formGetRequest("/tasks/byAttributes/", params); - fetch(process.env.REACT_APP_HOST_URL + url) - .then((response) => response.json()) - .then((taskData) => { - const namedTasks = taskData.map((item) => { - return { - ...item, - childName: childName, - childId: childId, - completed: completedTasks.includes(item._id), - }; - }); - const newTaskArray = [...taskArray, namedTasks]; - setTaskArray(newTaskArray); - }) - .catch((error) => console.log(error)); - }); + // get tasks based on children's attributes + const url = formGetRequest("/tasks/byAttributes/", params); + const taskResponse = await fetch(process.env.REACT_APP_HOST_URL + url); + const taskData = await taskResponse.json(); + const namedTasks = taskData.map((item) => { + return { + ...item, + childName: childName, + childId: childId, + completed: completedTasks.includes(item._id), + }; + }); + taskArray.push(namedTasks); }); + + await Promise.all(childPromises); // wait for all child promises to complete + + return { taskArray }; +}; + +export const logoutTimer = () => { + setTimeout(() => { + localStorage.removeItem("jwtToken"); + localStorage.removeItem("userID"); + return; + }, 15000); }; diff --git a/src/lib/utils.js b/src/lib/utils.js index 850c36a..bd380da 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -1,3 +1,12 @@ +import { COLORS_ARR } from "./constants"; + +/** + * @return a random color from the array + */ +export const getRandomColor = () => { + return COLORS_ARR[Math.floor(Math.random() * COLORS_ARR.length)]; +}; + /** * @param {String} string the string to be assessed * @return {Object} a json object containing the website links and text descriptions @@ -53,9 +62,9 @@ export const importCSVToJSON = (data) => { let obj = {}; for (let c = 0; c < data[r].length; ++c) { var content = data[r][c]; - // convert the string to array with spliterator "/" - if (content.includes("/")) { - content = content.split("/"); + // convert the string to array with spliterator "|" + if (content.includes("|")) { + content = content.split("|"); } obj[keys[c.toString()]] = content; } diff --git a/src/pages/AllTasks/index.js b/src/pages/AllTasks/index.js index 0689b8f..335819c 100644 --- a/src/pages/AllTasks/index.js +++ b/src/pages/AllTasks/index.js @@ -4,19 +4,36 @@ import styles from "./index.module.css"; import { AllTasksSection } from "../../components/AllTasksSection"; import ReactSearchBox from "react-search-box"; import { getChildrenTasksArray } from "../../lib/services"; +import { useNavigate } from "react-router-dom"; +import { TIMEOUT, WINDOW_TYPE } from "../../lib/constants"; +import { useWindowSize } from "../../lib/hooks"; +import Loader from "../LoadScreen"; const cx = classNames.bind(styles); export const AllTasks = () => { + const { width, type } = useWindowSize(); + const isMobile = type === WINDOW_TYPE.MOBILE; + const [loaded, setLoaded] = useState(false); const [allTaskArray, setAllTaskArray] = useState([]); const [searchQuery, setSearchQuery] = useState(""); - //TODO: get information using cache - const childrenId = ["63e5c4936d51fdbbbedb5503"]; + const childrenId = JSON.parse(localStorage.getItem("children")); + const [timer, setTimer] = useState(); - useEffect(() => { - getChildrenTasksArray(childrenId, false, allTaskArray, setAllTaskArray); + const navigate = useNavigate(); + + useEffect(async () => { + const { taskArray } = await getChildrenTasksArray(childrenId, false); + setAllTaskArray(taskArray); + setLoaded(true); }, []); + useEffect(() => { + return () => { + clearTimeout(timer); + }; + }, [timer]); + // handler for search box const handleSearch = (text) => { setSearchQuery(text); @@ -28,6 +45,23 @@ export const AllTasks = () => { }) ); + useEffect(() => { + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); + }, []); + + useEffect(() => { + return () => { + clearTimeout(timer); + }; + }, [timer]); + + if (!loaded) return ; + return (
{
{filteredSections.map((childTasks, childTasksIndex) => ( - + ))}
); }; - -//first two headers may go on separate lines -//check this for other code -//add search bar implementation** diff --git a/src/pages/Community/index.js b/src/pages/Community/index.js index d18834f..a854d12 100644 --- a/src/pages/Community/index.js +++ b/src/pages/Community/index.js @@ -3,121 +3,53 @@ import classNames from "classnames"; import styles from "./index.module.css"; import { BackArrow } from "../../components/BackArrow"; import { NavBar } from "../NavBar"; -import { CreatePost } from "../../components/CreatePost"; - +import { useNavigate } from "react-router-dom"; +import { TIMEOUT } from "../../lib/constants"; // component imports import Dropdown from "../../components/CommunityHeader"; import Post from "../../components/CommunityPost"; - //image imports import PFP from "../../images/PFP.png"; import PlusSign from "../../images/PlusSign.png"; - +import { CreatePost } from "../../components/CreatePost"; const cx = classNames.bind(styles); - export const Community = () => { - const [posts, setPosts] = useState([ - { - user: "blah", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - { - user: "blah2", - location: "nash", - img: PFP, - content: "lorem", - caption: "lorem", - comments: [ - { user: "user1", comment: "good comment" }, - { user: "user2", comment: "okay comment" }, - ], - }, - ]); - const [options, setOptions] = useState([ - "All Disabilities", - "ADHD", - "Autism", - ]); - const [showCreatePost, setShowCreatePost] = useState(false); - useEffect(() => { - fetch("/community") - .then((response) => response.json()) - .then((data) => { - setOptions(data.options); - setPosts(data.posts); + getPosts(); + }, []); + const getPosts = () => { + fetch(process.env.REACT_APP_HOST_URL + "/posts") + .then((response) => { + return response.json(); }) - .catch((error) => { - console.log(error.message); + .then((data) => { + console.log(data); + setPosts(data); }); + }; + const [posts, setPosts] = useState([]); + const [options, setOptions] = useState(["All Disabilities"]); + const [showCreatePost, setShowCreatePost] = useState(false); + const [rotate, setRotate] = useState(false); + const [timer, setTimer] = useState(); + const navigate = useNavigate(); + useEffect(() => { + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); }, []); - + useEffect(() => { + return () => { + clearTimeout(timer); + }; + }, [timer]); const createPost = () => { setShowCreatePost(!showCreatePost); + setRotate(!rotate); }; - return (
{
+ {showCreatePost && ( + + )}
- {posts.map((post) => { + {posts.reverse().map((post) => { return ( ); })}
-
+
{ + createPost(); + }}>
- {showCreatePost && }
); -}; +}; \ No newline at end of file diff --git a/src/pages/Community/index.module.css b/src/pages/Community/index.module.css index 3fa814c..1cec5cd 100644 --- a/src/pages/Community/index.module.css +++ b/src/pages/Community/index.module.css @@ -31,22 +31,48 @@ body { align-items: center; overflow: hidden; position: absolute; + cursor: pointer; right: 4vw; bottom: 10vh; z-index: 2; } +.posts { + margin-top: 10vh; +} + +.create_post img { + width: 70%; +} + .create_post:hover { cursor: pointer; - transform: scale(1.01); - transition: 0.2s; - box-shadow: 0 6px 4px darkgray; } -.posts { - margin-top: 10vh; + +.create_post_rotate { + width: 160px; + height: 160px; + background-color: #0298ba; + box-shadow: 0 2px 16px 0 gray; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + position: absolute; + cursor: pointer; + right: 4vw; + bottom: 10vh; + z-index: 2; + transform: rotate(45deg); + transition: transform 0.1s ease-in-out; } -.create_post img { +.create_post_rotate img { width: 70%; } + +.create_post_rotate:hover { + cursor: pointer; +} \ No newline at end of file diff --git a/src/pages/Home/index.js b/src/pages/Home/index.js index 62254a0..d0f2d40 100644 --- a/src/pages/Home/index.js +++ b/src/pages/Home/index.js @@ -1,111 +1,165 @@ -import React, { useState, useEffect, createContext } from "react"; +import React, { useEffect, useState } from "react"; import classNames from "classnames/bind"; import styles from "./index.module.css"; import { NavBar } from "../NavBar"; -import { ROUTES } from "../../lib/constants"; import { useWindowSize } from "../../lib/hooks"; import { WINDOW_TYPE } from "../../lib/constants"; - -import { - CircularProgressbarWithChildren, - buildStyles, -} from "react-circular-progressbar"; -import "react-circular-progressbar/dist/styles.css"; +import { OnYourRadar } from "../../components/OnYourRadar"; +import { Slider } from "../../components/Slider"; +import ProgressBar from "@ramonak/react-progress-bar"; +import { formGetRequest, getAgeGivenBirthday } from "../../lib/utils"; +import { AuthButton } from "../../components/AuthButton"; +import { useNavigate } from "react-router-dom"; +import { TIMEOUT } from "../../lib/constants"; +import { Loader } from "../LoadScreen"; const cx = classNames.bind(styles); export const Home = () => { const { width, type } = useWindowSize(); - const isMobile = type === WINDOW_TYPE.MOBILE; + const [lastName, setLastName] = useState(localStorage.getItem("lastName")); + const [totalPoints, setTotalPoints] = useState(0); + const [totalGoal, setTotalGoal] = useState(0); + const [childrenStats, setChildrenStats] = useState([]); + const childrenId = JSON.parse(localStorage.getItem("children")); + const [timer, setTimer] = useState(); + + const navigate = useNavigate(); + + const logout = () => { + localStorage.clear(); + navigate("/login"); + }; + + const getChildrenPointsStats = async () => { + let totalPoints = 0; + let totalGoal = 0; + let childrenStats = []; + const childPromises = childrenId.map(async (childId) => { + // get child's name and disabilities + const childUrl = "/children/" + childId; + const response = await fetch(process.env.REACT_APP_HOST_URL + childUrl); + const childData = await response.json(); + const childName = childData.firstName; + const age = getAgeGivenBirthday(childData.birthDate); + totalPoints += childData.completedTasks.length; + const params = { + disabilities: JSON.stringify(childData.disabilities), + age: JSON.stringify(age), + }; + + // get tasks based on children's attributes + const url = formGetRequest("/tasks/byAttributes/", params); + const taskResponse = await fetch(process.env.REACT_APP_HOST_URL + url); + const taskData = await taskResponse.json(); + totalGoal += taskData.length; + const childStats = { + childId, + childName, + points: childData.completedTasks.length, + goal: taskData.length, + }; + childrenStats.push(childStats); + }); - const [lastName, setLastName] = useState("Adam's"); - const [goal, setGoal] = useState(100); - const [points, setPoints] = useState(71); - const [hpList, sethpList] = useState(["Medicaid Waitlist"]); - const [elseList, setElseList] = useState([ - "Register for Autism Symposium", - "Intensive IEP support & training", - ]); + await Promise.all(childPromises); // wait for all child promises to complete + + return { totalPoints, totalGoal, childrenStats }; + }; useEffect(() => { - fetch("/userData") - .then((response) => response.json()) - .then((data) => { - setLastName(data.lastName); - setGoal(data.goal); - setPoints(data.points); - sethpList(data.hpList); - setElseList(data.elseList); - }) - .catch((error) => console.log(error)); + return () => { + clearTimeout(timer); + }; + }, [timer]); + + useEffect(async () => { + const { totalPoints, totalGoal, childrenStats } = + await getChildrenPointsStats(); + setTotalPoints(totalPoints); + setTotalGoal(totalGoal); + setChildrenStats(childrenStats); + console.log(localStorage); + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); }, []); - const hpElements = hpList.map((thing, index) => ( -

- {index + 1 + ". " + thing} -

- )); - const elseElements = elseList.map((thing, index) => ( -

- {index + 1 + ". " + thing} -

- )); + if (childrenStats.length !== childrenId.length) { + return ; + } return ( -
+
-
-

Welcome 

-

{lastName} Family!

+
+

+ Welcome  +

+

+ {lastName} Family! +

-

You're 

-

Cruising it!

-
-
-

{goal - points}

-

-  points away from your weekly goal +

+ You're 

-
-
- -
{points}
-
Points
-
-
-
-

On Your Radar

-

High Priority

- {hpElements} -

Everything Else

- {elseElements} - + Cruising it! +

+ + + + logout()} + isMobile={isMobile} + />
-
+
); }; - -//issues: -//what happens when todo list becomes larger than the screen -//or family last name is too long -//fix navbar for computer screen (looks bad on screen anyways) -//spacing can be tweaked diff --git a/src/pages/Home/index.module.css b/src/pages/Home/index.module.css index 1dd2946..941b2ee 100644 --- a/src/pages/Home/index.module.css +++ b/src/pages/Home/index.module.css @@ -1,118 +1,76 @@ @import url('https://fonts.googleapis.com/css?family=Poppins'); .container { - width: 100%; + width: 70%; + margin: auto; box-sizing: border-box; - padding: 70px 40px 80px 40px; + padding: 2vw 2vw 10vw 2vw; } + .container.mobile { width: 100%; - padding: 2vw 2vw 2vw 2vw; + box-sizing: border-box; + padding: 2vw 2vw 10vw 2vw; } + .text_div { margin-left: auto; margin-right: auto; text-align: center; position: relative; } + .text_div.first { - margin-top: 10vw; + margin-top: 5vw; } + .text_div.second { margin-top: 3vw; } .welcome { font-family: 'Poppins'; font-style: normal; - font-size: 7vw; + font-size: 3vw; margin: auto; display: inline-block; color: #A8A8A8; } -.welcome.family { - color: black; - font-weight: 900; -} - -.cruising { +.welcome.mobile { font-family: 'Poppins'; font-style: normal; - font-size: 5vw; + font-size: 7vw; margin: auto; display: inline-block; color: #A8A8A8; } -.cruising.color { - color: #E3D150; +.welcome.family { + color: black; font-weight: 900; } -.points { +.cruising { font-family: 'Poppins'; - font-weight: 800; - font-size: 4.2vw; + font-style: normal; + font-size: 2vw; margin: auto; display: inline-block; - color: #000000; -} - -.points.text { - font-weight: 600; - color: #818181; -} - -.progress_circle { - width: 40vw; - height: 40vw; - margin-top: 5vw; - margin-bottom: 5vw; - margin-left: auto; - margin-right: auto; + color: #A8A8A8; } -.progress_circle_text { +.cruising.mobile { font-family: 'Poppins'; - font-weight: 900; - color: black; + font-style: normal; font-size: 6vw; + margin: auto; + display: inline-block; + color: #A8A8A8; } -.todo_div { - border: 6px #0198BA; - border-style: solid; - border-radius: 50px; - margin: 2vw; - margin-bottom: 4vw; - padding: 3vw; -} - -.radar { - font-family: 'Poppins'; - font-weight: 900; - color: black; - font-size: 7vw; - margin-top: 3vw; -} - -.priority { - font-family: 'Poppins'; - font-weight: bolder; - color: #8B5674; - font-size: 5vw; - margin-top: 6vw; -} - -.priority.else { +.cruising.color { color: #E3D150; -} - -.list { - font-family: 'Poppins'; - color: black; - font-weight: 600; - font-size: 3vw; + font-weight: 900; } .link_div { @@ -127,7 +85,4 @@ color: #0198BA; font-size: 3vw; font-weight: 400; -} - -/* need to fix font later */ -/*Not set to percentage*/ \ No newline at end of file +} \ No newline at end of file diff --git a/src/pages/HomeLayout/index.js b/src/pages/HomeLayout/index.js index 891f48e..0908ed4 100644 --- a/src/pages/HomeLayout/index.js +++ b/src/pages/HomeLayout/index.js @@ -37,7 +37,7 @@ export const HomeLayout = () => { {" "}
)} - + { - const navigate = useNavigate(); - return (
- +
); diff --git a/src/pages/Login/index.js b/src/pages/Login/index.js index cf347e6..eb8afbd 100644 --- a/src/pages/Login/index.js +++ b/src/pages/Login/index.js @@ -4,6 +4,7 @@ import classNames from "classnames/bind"; import { ReactComponent as ReactLogo } from "../../svg/F2F-logo.svg"; import styles from "./index.module.css"; import { ROUTES } from "../../lib/constants"; +import { STATUS_CODE } from "../../lib/constants"; const cx = classNames.bind(styles); @@ -14,21 +15,21 @@ export const Login = () => { const [error, setError] = useState(""); const [isLoggedIn, setIsLoggedIn] = useState(false); const [rememberMe, setRememberMe] = useState(false); - const [userID, setUserID] = useState(-1); const navigate = useNavigate(); useEffect(() => { if (isLoggedIn) { - navigate("/home", { - state: { - email: email, - userID: userID, - }, - }); + navigate("/home"); } }, [isLoggedIn, navigate]); useEffect(() => { + const userID = checkIfLoggedIn(); + console.log(userID); + if (userID) { + navigate("/home"); + } + // Check if there is a rememberMeToken in the localStorage const rememberMeToken = JSON.parse(localStorage.getItem("rememberMeToken")); if (rememberMeToken) { @@ -42,7 +43,7 @@ export const Login = () => { setIsLoading(true); setError(""); - fetch("http://localhost:3001/users/login", { + fetch(process.env.REACT_APP_HOST_URL + "/users/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: email, password: password }), @@ -51,10 +52,13 @@ export const Login = () => { return response.json(); }) .then((res) => { - console.log(res); - if (res.message === "SUCCESS") { + if (res.message === STATUS_CODE.SUCESS) { + localStorage.setItem("userID", res.id); + localStorage.setItem("jwtToken", res.token); + localStorage.setItem("firstName", res.firstName); + localStorage.setItem("lastName", res.lastName); + localStorage.setItem("children", JSON.stringify(res.children)); setIsLoggedIn(true); - setUserID(res.userID); } else { setError(res.message); } @@ -73,6 +77,19 @@ export const Login = () => { setIsLoading(false); }; + const checkIfLoggedIn = () => { + // console.log(localStorage.getItem("jwtToken")); + const rememberMeToken = localStorage.getItem("jwtToken"); + console.log(rememberMeToken); + if (rememberMeToken) { + // User is logged in + return rememberMeToken; + } else { + // User is not logged in + return null; + } + }; + return (
@@ -116,7 +133,8 @@ export const Login = () => { + style={{ textDecoration: "none", color: "rgb(2, 152, 186)" }} + > Forgot Password @@ -134,7 +152,8 @@ export const Login = () => { Don't have an account?  + style={{ textDecoration: "none", color: "rgb(2, 152, 186)" }} + > Sign up diff --git a/src/pages/NavBar/index.js b/src/pages/NavBar/index.js index 6ebbac9..abe61e0 100644 --- a/src/pages/NavBar/index.js +++ b/src/pages/NavBar/index.js @@ -2,7 +2,7 @@ import styles from "./index.module.css"; import classNames from "classnames/bind"; import { useLocation, useNavigate } from "react-router-dom"; import { useWindowSize } from "../../lib/hooks"; -import { HOME_NAV_LABELS, ROUTES, WINDOW_TYPE } from "../../lib/constants"; +import { ROUTES, WINDOW_TYPE } from "../../lib/constants"; import { HomeIcon, RoadmapIcon, CommunityIcon } from "../../lib/icons"; const cx = classNames.bind(styles); @@ -12,7 +12,6 @@ export const NavBar = ({ showNavBar = true }) => { const isMobile = type === WINDOW_TYPE.MOBILE; const navigate = useNavigate(); const { pathname } = useLocation(); - const isSmall = width < 600 || isMobile; const goTo = (route) => { navigate(route); @@ -26,21 +25,41 @@ export const NavBar = ({ showNavBar = true }) => { [styles.mobile]: isMobile, })} > -
+
goTo(ROUTES.HOME)} - className={cx(styles.navItem)} + className={cx(styles.navItem, { + [styles.mobile]: isMobile, + })} > -
- +
+
goTo(ROUTES.ROADMAP)} - className={cx(styles.navItem)} + className={cx(styles.navItem, { + [styles.mobile]: isMobile, + })} > -
+
{
goTo(ROUTES.COMMUNITY)} - className={cx(styles.navItem)} + className={cx(styles.navItem, { + [styles.mobile]: isMobile, + })} > -
- +
+
diff --git a/src/pages/NavBar/index.module.css b/src/pages/NavBar/index.module.css index 42b5f10..37a5cf0 100644 --- a/src/pages/NavBar/index.module.css +++ b/src/pages/NavBar/index.module.css @@ -1,46 +1,78 @@ .page { - width: 100%; - display: flex; + margin: 0; + width: 100%; + display: flex; } - -.page.module { - margin: 0; - width: 100%; - display: flex; +.page.mobile { + margin: 0; + width: 100%; } - .navBar { - box-shadow: 1px -5px 10px #D9D9D9; - width: 100%; - height: 8vh; - left: 0; - bottom: 0; - position: absolute; - display: flex; - z-index: 100; + box-shadow: 1px -5px 10px #D9D9D9; + margin: auto; + width: 100%; + height: 7.5vh; + left: 0; + position: fixed; + display: flex; + z-index: 100; + bottom: 0; +} +<<<<<<< HEAD + +.navBar.mobile { + height: 10vh; } -@media screen and (min-width: 1000px) { +/* @media screen and (min-width: 1000px) { .navBar { top: 0; + height: 15vh; } - } + } */ +======= +.navBar.mobile { + height: 10vh; +} +/* @media screen and (min-width: 1000px) { + .navBar { + top: 0; + height: 15vh; + } +} */ +>>>>>>> 3654b8cf76b0bf140e1717ec3f84cfce70c80d45 +.navItem.mobile { + box-sizing: border-box; + width: 33.333%; + height: 100%; + background-color: #FFFFFF; + padding: 5px 10px; + align-items: center; + display: flex; + justify-content: center; + margin-bottom: 20px; + cursor: pointer; +} .navItem { - box-sizing: border-box; - width: 33.333%; - height: 100%; - background-color: #ffffff; - padding: 5px 10px; - align-items: center; - display: flex; - justify-content: center; - margin-bottom: 20px; - cursor: pointer; + box-sizing: border-box; + width: 33.333%; + height: 100%; + background-color: #FFFFFF; + padding: 5px 10px; + align-items: center; + display: flex; + justify-content: center; + margin-bottom: 20px; + cursor: pointer; +} +.icon_div.mobile { + text-align: center; + display: flex; + justify-content: center; } - .icon_div { - text-align: center; - display: flex; - justify-content: center; + text-align: center; + display: flex; + justify-content: center; } \ No newline at end of file diff --git a/src/pages/Roadmap/index.js b/src/pages/Roadmap/index.js index 52918fa..f54ade7 100644 --- a/src/pages/Roadmap/index.js +++ b/src/pages/Roadmap/index.js @@ -1,10 +1,8 @@ -import React, { useState, useEffect, useRef, createContext } from "react"; +import React, { useState, useEffect, useRef } from "react"; import classNames from "classnames/bind"; import Papa from "papaparse"; -import { useAuth } from "../../lib/AuthContext"; import styles from "./index.module.css"; -import { ROUTES, PRIORITY_LEVEL } from "../../lib/constants"; -import { getChildrenTasksArray } from "../../lib/services"; +import { ROUTES, PRIORITY_LEVEL, TIMEOUT } from "../../lib/constants"; import { ReactComponent as Calender } from "../../svg/roadmapCalender.svg"; import { ReactComponent as Box } from "../../svg/roadmapBox.svg"; import { useNavigate } from "react-router-dom"; @@ -14,54 +12,71 @@ import { getAgeGivenBirthday, } from "../../lib/utils"; import { AuthButton } from "../../components/AuthButton"; +import { useWindowSize } from "../../lib/hooks"; +import { WINDOW_TYPE } from "../../lib/constants"; +import Loader from "../LoadScreen"; const cx = classNames.bind(styles); export const Roadmap = ({ toast }) => { + const { width, type } = useWindowSize(); + const isMobile = type === WINDOW_TYPE.MOBILE; const navigate = useNavigate(); + const [loaded, setLoaded] = useState(false); const [numTasks, setNumTasks] = useState(0); const [numAllTasks, setNumAllTasks] = useState(0); const uploadRef = useRef(); const [importFile, setImportFile] = useState(null); - const [hpList, sethpList] = useState([]); - const [elseList, setElseList] = useState([]); - //TODO: get the information from cache - const childrenId = ["63e5c4936d51fdbbbedb5503"]; + const childrenId = JSON.parse(localStorage.getItem("children")); + const [timer, setTimer] = useState(); + const userId = localStorage.getItem("userID"); - const getStats = (childrenId) => { - childrenId.forEach((childId) => { + const getStats = async (childrenId) => { + let childrenStatsData = { numUpcoming: 0, numAll: 0 }; + const childPromises = childrenId.map(async (childId) => { // get child's name and disabilities const childUrl = "/children/" + childId; - fetch(process.env.REACT_APP_HOST_URL + childUrl) - .then((response) => response.json()) - .then((childrenData) => { - const childName = childrenData.firstName; - const age = getAgeGivenBirthday(childrenData.birthDate); - const completedTasks = childrenData.completedTasks; - const params = { - disabilities: JSON.stringify(childrenData.disabilities), - age: JSON.stringify(age), - //TODO: fix the data for upcoming vs priority - priority: JSON.stringify(2), - }; - - // get tasks based on children's attributes - const url = formGetRequest("/tasks/getStats/", params); - fetch(process.env.REACT_APP_HOST_URL + url) - .then((response) => response.json()) - .then((data) => { - setNumTasks(data.numUpcoming); - setNumAllTasks(data.numAll); - }) - .catch((error) => console.log(error)); - }); + const response = await fetch(process.env.REACT_APP_HOST_URL + childUrl); + const childData = await response.json(); + const age = getAgeGivenBirthday(childData.birthDate); + const params = { + disabilities: JSON.stringify(childData.disabilities), + age: JSON.stringify(age), + priority: JSON.stringify(PRIORITY_LEVEL.PRIORITY_LEVEL), + }; + + // get tasks based on children's attributes + const url = formGetRequest("/tasks/getStats/", params); + const statsResponse = await fetch(process.env.REACT_APP_HOST_URL + url); + const statsData = await statsResponse.json(); + childrenStatsData.numUpcoming += statsData.numUpcoming; + childrenStatsData.numAll += statsData.numAll; }); + + await Promise.all(childPromises); // wait for all child promises to complete + return childrenStatsData; }; + useEffect(async () => { + const { numUpcoming, numAll } = await getStats(childrenId); + setNumAllTasks(numAll); + setNumTasks(numUpcoming); + setLoaded(true); + }, []); + useEffect(() => { - getStats(childrenId); - getChildrenTasksArray(childrenId, true, hpList, sethpList); - getChildrenTasksArray(childrenId, false, elseList, setElseList); + return () => { + clearTimeout(timer); + }; + }, [timer]); + + useEffect(() => { + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); }, []); // set the import csv file @@ -82,7 +97,8 @@ export const Roadmap = ({ toast }) => { headers: { "Content-Type": "application/json" }, body: JSON.stringify(inputObj), }) - .then((response) => console.log(response.json())) + .then((response) => response.json()) + .then((data) => toast("Successfully uploaded the csv!")) .catch((error) => { console.error(error); }); @@ -100,7 +116,8 @@ export const Roadmap = ({ toast }) => { }; const onExport = async () => { - fetch("http://localhost:3001/users/exportCSV") + const exportUrl = "/users/exportCSV"; + fetch(process.env.REACT_APP_HOST_URL + exportUrl) .then((response) => response.arrayBuffer()) .then((arrayBuffer) => { const blob = new Blob([arrayBuffer], { type: "text/csv" }); @@ -114,16 +131,7 @@ export const Roadmap = ({ toast }) => { .catch((error) => console.error(error)); }; - const hpElements = hpList.flat().map((thing, index) => ( -

- {index + 1 + ". " + thing.title} -

- )); - const elseElements = elseList.flat().map((thing, index) => ( -

- {index + 1 + ". " + thing.title} -

- )); + if (!loaded) return ; return (
{ overflow: "scroll", overscrollBehavior: "none", height: "80vh", - }}> - {/*
-

Road Map

-

- Below are all the tasks needed to be completed -

-
*/} - -
- - setImportFile(e.target.files[0])} - /> -
-
- -
+ }} + > + {userId === process.env.REACT_APP_ADMIN_ID && ( +
+
+ + setImportFile(e.target.files[0])} + /> +
+
+ +
+
+ )}
navigate(ROUTES.UPCOMING_TASKS)} - className={cx(styles.tasks_div)}> + className={cx(styles.tasks_div, { + [styles.mobile]: isMobile, + })} + >
- +
-

Upcoming

+ }} + > +

+ High Priority +

-

- All tasks with priority level 2 +

+ All tasks with priority level higher than 2

-

{numTasks}

+

+ {numTasks} +

navigate(ROUTES.ALL_TASKS)} - className={cx(styles.tasks_div, "all")}> + className={cx(styles.tasks_div, "all", { + [styles.mobile]: isMobile, + })} + >
- +
-

All Tasks

+ }} + > +

+ All Tasks +

-

Everything on the list

+

+ Everything on the list +

-

{numAllTasks}

+

+ {numAllTasks} +

- - {/* just for mvp */} -
-

On Your Radar

-

High Priority

- {hpElements} -

All Tasks

- {elseElements} -
); }; - -//Make each box into a compenent (lot of redundant code)** (move to components folder) diff --git a/src/pages/Roadmap/index.module.css b/src/pages/Roadmap/index.module.css index 7773310..dae6bc0 100644 --- a/src/pages/Roadmap/index.module.css +++ b/src/pages/Roadmap/index.module.css @@ -3,20 +3,31 @@ .header { font-family: "Poppins"; font-style: normal; - font-size: 8vw; + font-size: 1vw; font-weight: 900; display: inline-block; color: black; margin-bottom: 0; } -.header.small { - font-size: 3vw; +.header.mobile { + font-size: 4vw; font-weight: normal; margin-top: 0; } .tasks_div { + border: 1px black; + border-style: solid; + border-radius: 50px; + margin-top: 8vw; + margin-left: 3vw; + margin-right: 3vw; + padding: 3vw; + background-color: #dbf0f5; +} + +.tasks_div.mobile { border: 4px black; border-style: solid; border-radius: 50px; @@ -32,6 +43,11 @@ } .icon { + height: 3vw; + width: 3vw; +} + +.icon.mobile { height: 12vw; width: 12vw; } @@ -45,56 +61,25 @@ display: inline-block; font-family: "Poppins"; font-style: normal; - font-size: 7vw; + font-size: 2vw; font-weight: 900; } +.taskDesc.mobile { + font-size: 7vw; +} + .taskNum { display: inline-block; font-family: "Poppins"; font-style: normal; - font-size: 6vw; + font-size: 2vw; font-weight: bolder; margin-left: auto; } -/* just for mvp */ -.todo_div { - border: 6px #0198ba; - border-style: solid; - border-radius: 50px; - margin-top: 10vw; - margin-left: 2vw; - margin-right: 2vw; - padding-left: 3vw; - padding-bottom: 10vw; -} - -.radar { - font-family: "Poppins"; - font-weight: 900; - color: black; - font-size: 7vw; - margin-top: 3vw; -} - -.priority { - font-family: "Poppins"; - font-weight: bolder; - color: #8b5674; - font-size: 5vw; - margin-top: 6vw; -} - -.priority.else { - color: #e3d150; -} - -.list { - font-family: "Poppins"; - color: black; - font-weight: 600; - font-size: 3vw; +.taskNum.mobile { + font-size: 6vw; } .csvButton { diff --git a/src/pages/SignUp/index.js b/src/pages/SignUp/index.js index a163e75..6e97b4c 100644 --- a/src/pages/SignUp/index.js +++ b/src/pages/SignUp/index.js @@ -4,21 +4,25 @@ import { AUTH_INPUT_LABELS, SCHOOL_DISTRICT, DISABILITY, + ROUTES, WINDOW_TYPE, STATUS_CODE, - ROUTES, } from "../../lib/constants"; import { AuthInputBlock } from "../../components/AuthInputBlock"; import { AuthSelectBlock } from "../../components/AuthSelectBlock"; import { AuthButton } from "../../components/AuthButton"; -import { useEffect, useState } from "react"; -import { signUp, sendVerificationEmail } from "../../lib/services"; +import { useState, useEffect } from "react"; +import { signUp, signUpChildren, mongoCheck } from "../../lib/services"; import { useNavigate } from "react-router-dom"; import { useWindowSize } from "../../lib/hooks"; -import { mongoCheck } from "../../lib/services"; - +import { AddChild } from "../../components/AddChild"; const cx = classNames.bind(styles); +//CHANGE THE BOXES FOR EMPTY CHILD +// WHERE IS OUR VERIFICATION +// ADD CHECKS FOR CHILD INFORMATION/HAS ALL CHILD INFORMATION +// DELETE CHILD/EDIT CHILD/VIEW CHILD + // Register page for authentication export const SignUp = ({ toast }) => { const navigate = useNavigate(); @@ -32,31 +36,46 @@ export const SignUp = ({ toast }) => { const [disability, setDisability] = useState([]); const [zipCode, setZipCode] = useState(""); const [phoneNumber, setPhoneNumber] = useState(""); - const [verifCode, setVerifCode] = useState(-1); + const [children, setChildren] = useState([]); + + const [addChildPopUp, setAddChildPopUp] = useState(false); + const [childIDArray, setChildIDArray] = useState([]); + const [registered, setRegistered] = useState(false); const [error, setError] = useState(""); + const [renderedChildren, setRenderedChildren] = useState(); - useEffect(() => { - console.log(verifCode); - //navigate or make visibl - }, [verifCode]); + async function addChildren() { + try { + const promises = children.map(async (child) => { + let disArray = []; + for (let i = 0; i < child.disabilities.length; i++) { + disArray.push(child.disabilities[i].label); + } + let fn = child.firstName; + let bd = child.birthDate; + let sd = child.schoolDistrict; + const result = await signUpChildren({ + firstName: fn, + birthDate: bd, + disabilities: disArray, + schoolDistrict: sd, + }); + return result; + }); + const results = await Promise.all(promises); + console.log("returned child ids:", results); + setChildIDArray(results); + } catch (error) { + console.log(error); + } + } - const onRegister = () => { - if (!email) toast("Please provide your email"); - else if (!password) toast("Please provide your password"); - else if (password.length < 8) toast("Password length must be at least 8"); - else if (password !== repeatPassword) toast("Passwords mismatch"); - else if (!firstName) toast("Please provide your first name"); - else if (!lastName) toast("Please provide your last name"); - else if (!schoolDistrict) - toast("Please provide your children's school district"); - else if (!disability) toast("Please provide your children's disabilities"); - else if (!zipCode) toast("Please provide your zip code"); - else if (!phoneNumber) toast("Please provide your phone number"); - else { + useEffect(() => { + if (childIDArray.length === children.length && registered) { mongoCheck(email).then((res) => { - console.log(res); if (res.status === "Found") { setError("Email already exists"); + toast(error); } else { navigate(ROUTES.VERIFICATION, { state: { @@ -67,45 +86,105 @@ export const SignUp = ({ toast }) => { schoolDistrict: schoolDistrict, zipCode: zipCode, phoneNumber: phoneNumber, + children: childIDArray, }, }); } }); + } + }, [childIDArray]); + + const onRegister = async () => { + if (!email) toast("Please provide your email"); + else if (!password) toast("Please provide your password"); + else if (password.length < 8) toast("Password length must be at least 8"); + else if (password !== repeatPassword) toast("Passwords mismatch"); + else if (!firstName) toast("Please provide your first name"); + else if (!lastName) toast("Please provide your last name"); + else if (!zipCode) toast("Please provide your zip code"); + else if (!phoneNumber) toast("Please provide your phone number"); + else if (children.length === 0 || children === []) + toast("Please add at least one child"); + else { // send response to backend and create a record - // signUp({ - // email, - // password, - // firstName, - // lastName, - // schoolDistrict, - // zipCode, - // phoneNumber, - // }) - // .then((res) => { - // // console.log(res); - // // navigate("/login") - // // let message = res.message; - // // //make constants later - // // console.log(message); - // // if (message === "toLogin") { - // // console.log("1"); - // // navigate("/login"); - // // } - // // if (message === "sendVerification") { - // // sendVerificationEmail(email).then((code) => { - // // setVerifCode(code); - // // }); - // // //if input code === code , we continue - // // } - // // if (message === "sendSFForm") { - // // console.log("3"); - // // // navigate("/createUser") - // // } - // }) - // .catch((err) => toast("Internal error")); + addChildren(); + setRegistered(true); } }; + function handleClick(i) { + children.splice(i, 1); + renderChildrenFunc(); + } + + useEffect(() => { + renderChildrenFunc(); + }, [children]); + + function renderChildrenFunc() { + const inputs = []; + if (children.length >= 1) { + for (let j = 0; j < children.length / 3; j++) { + const array = []; + for (let i = j * 3; i < children.length && i < j * 3 + 3; i++) { + const child = children[i]; + array.push( +
+
+

+ {child.firstName} +

+ +
+
+ ); + } + inputs.push( +
+ {array[0]} + {array.length >= 1 && array[1]} + {array.length >= 2 && array[2]} +
+ ); + } + } + setRenderedChildren(inputs); + } + return ( <> { onChange={setLastName} isMobile={isMobile} /> - - { onChange={setPhoneNumber} isMobile={isMobile} /> - {error &&
{error}
} + {renderedChildren} + setAddChildPopUp(true)} + isMobile={isMobile} + /> + {addChildPopUp && ( + + )} { const [resources, setResources] = useState([]); const [checked, setChecked] = useState(completed); + const [timer, setTimer] = useState(); + + const navigate = useNavigate(); + + useEffect(() => { + console.log(localStorage); + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); + }, []); + + useEffect(() => { + return () => { + clearTimeout(timer); + }; + }, [timer]); useEffect(() => { const url = "/tasks/" + taskId; @@ -69,6 +89,3 @@ export const TaskDetails = () => {
); }; - -// need to import image/svg from backend -// for page and each resource card diff --git a/src/pages/Upcoming/index.js b/src/pages/Upcoming/index.js index 80af95a..9af9632 100644 --- a/src/pages/Upcoming/index.js +++ b/src/pages/Upcoming/index.js @@ -3,8 +3,11 @@ import classNames from "classnames/bind"; import { UpcomingComponent } from "../../components/UpcomingComponent"; import { useWindowSize } from "../../lib/hooks"; import { useState, useEffect } from "react"; -import { WINDOW_TYPE } from "../../lib/constants"; +import { WINDOW_TYPE, TIMEOUT } from "../../lib/constants"; import { getChildrenTasksArray } from "../../lib/services"; +import { separateWebsiteLink } from "../../lib/utils"; +import { useNavigate } from "react-router-dom"; +import { Loader } from "../LoadScreen"; const cx = classNames.bind(styles); @@ -12,17 +15,40 @@ const cx = classNames.bind(styles); export const Upcoming = ({ toast }) => { const isMobile = useWindowSize().type === WINDOW_TYPE.MOBILE; const [taskArray, setTaskArray] = useState([]); + const [loaded, setLoaded] = useState(false); //TODO: get information using cache - const childrenId = ["63e5c4936d51fdbbbedb5503"]; + const childrenId = JSON.parse(localStorage.getItem("children")); + const [timer, setTimer] = useState(); - useEffect(() => { - getChildrenTasksArray(childrenId, true, taskArray, setTaskArray); + const navigate = useNavigate(); + + useEffect(async () => { + const { taskArray } = await getChildrenTasksArray(childrenId, true); + setTaskArray(taskArray); + setLoaded(true); }, []); const deduplicatedList = taskArray.flat().filter((obj, index, self) => { return index === self.findIndex((o) => o._id === obj._id); }); + useEffect(() => { + setTimer( + setTimeout(() => { + localStorage.clear(); + navigate("/login"); + }, TIMEOUT) + ); + }, []); + + useEffect(() => { + return () => { + clearTimeout(timer); + }; + }, [timer]); + + if (!loaded) return ; + return ( <>
@@ -32,10 +58,11 @@ export const Upcoming = ({ toast }) => { taskId={task._id} title={task.title} time={task.timePeriod} - content={task.details} + content={separateWebsiteLink(task.details).otherStrings} completed={task.completed} priority={task.priority} childId={task.childId} + childName={task.childName} isMobile={isMobile} /> ))} diff --git a/testRequests.rest b/testRequests.rest index a17c335..c92bcc4 100644 --- a/testRequests.rest +++ b/testRequests.rest @@ -23,12 +23,12 @@ Content-Type: application/json ### -GET http://localhost:3001/children/63e5c4936d51fdbbbedb5503 +GET http://localhost:3001/children/63e5c4296d51fdbbbedb5500 Content-Type: application/json ### -GET http://localhost:3001/children/63d58b72c2e0063e64001a1e +GET http://localhost:3001/children/643b22b6ee8225a6684ac15b Content-Type: application/json @@ -46,7 +46,7 @@ Content-Type: application/json ### -GET http://localhost:3001/tasks/byAttributes/?disabilities=["ADHD","disability2"]&age=30 +GET http://localhost:3001/tasks/byAttributes/?disabilities=["ADHD","disability2"] Content-Type: application/json ### @@ -54,13 +54,18 @@ POST http://localhost:3001/tasks Content-Type: application/json { - "title": "Task 3", + "title": "Task 5", "details": "Call LIDDA based on county|https://resources.hhs.texas.gov/pages/find-services|https://resources.hhs.texas.gov/pages", "disabilities": ["ADHD", "disability1", "disability2"], "timePeriod": "monthly", - "age": "Adult" + "age": "Adult", + "priority": 1 } ### -GET http://localhost:3001/tasks +GET http://localhost:3001/tasks/byAttributes/?disabilities=["ADHD/ADD"]&age="Adult" +Content-Type: application/json + +### +GET http://localhost:3001/tasks/getStats/?disabilities=["ADHD/ADD"]&age="Adult"&priority=2 Content-Type: application/json \ No newline at end of file