diff --git a/.idx/dev.nix b/.idx/dev.nix new file mode 100644 index 0000000..1ad5b66 --- /dev/null +++ b/.idx/dev.nix @@ -0,0 +1,59 @@ +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{ pkgs, ... }: { + # Which nixpkgs channel to use. + channel = "stable-23.11"; # or "unstable" + + # Use https://search.nixos.org/packages to find packages + packages = [ + # pkgs.go + # pkgs.python311 + # pkgs.python311Packages.pip + # pkgs.nodejs_20 + # pkgs.nodePackages.nodemon + pkgs.gh + ]; + + # Sets environment variables in the workspace + env = {}; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + # "vscodevim.vim" + "naumovs.color-highlight" + "dsznajder.es7-react-js-snippets" + "esbenp.prettier-vscode" + ]; + + # Enable previews + previews = { + enable = true; + previews = { + # web = { + # # Example: run "npm run dev" with PORT set to IDX's defined port for previews, + # # and show it in IDX's web preview panel + # command = ["npm" "run" "dev"]; + # manager = "web"; + # env = { + # # Environment variables to set for your server + # PORT = "$PORT"; + # }; + # }; + }; + }; + + # Workspace lifecycle hooks + workspace = { + # Runs when a workspace is first created + onCreate = { + # Example: install JS dependencies from NPM + # npm-install = "npm install"; + }; + # Runs when the workspace is (re)started + onStart = { + # Example: start a background task to watch and re-build backend code + # watch-backend = "npm run watch-backend"; + }; + }; + }; +} diff --git a/app/index.html b/app/index.html index 4d8a087..c701ec6 100644 --- a/app/index.html +++ b/app/index.html @@ -1,5 +1,5 @@ - + diff --git a/app/package-lock.json b/app/package-lock.json index 6c4c811..9f117e3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -10,12 +10,17 @@ "dependencies": { "@emoji-mart/data": "^1.1.2", "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@iconscout/react-unicons": "^2.0.2", + "@mui/material": "^5.15.16", + "@mui/x-date-pickers": "^7.3.1", "axios": "^1.6.8", "bootstrap": "^5.3.3", + "dayjs": "^1.11.11", "dompurify": "^3.1.0", "emoji-mart": "^5.5.2", "github-markdown-css": "^5.5.1", @@ -23,8 +28,11 @@ "marked": "^12.0.1", "react": "^18.2.0", "react-bootstrap": "^2.10.2", + "react-circular-progressbar": "^2.1.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.3" + "react-router-dom": "^6.22.3", + "react-select": "^5.8.0", + "react-slider": "^2.0.6" }, "devDependencies": { "@types/react": "^18.2.64", @@ -63,7 +71,6 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", - "dev": true, "dependencies": { "@babel/highlight": "^7.24.2", "picocolors": "^1.0.0" @@ -180,7 +187,6 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.1.tgz", "integrity": "sha512-HfEWzysMyOa7xI5uQHc/OcZf67/jc+xe/RZlznWQHhbb8Pg1SkRdbK4yEi61aY8wxQA7PkSfoojtLQP/Kpe3og==", - "dev": true, "dependencies": { "@babel/types": "^7.24.0" }, @@ -244,7 +250,6 @@ "version": "7.24.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -253,7 +258,6 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -285,7 +289,6 @@ "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.2.tgz", "integrity": "sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -388,7 +391,6 @@ "version": "7.24.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -412,6 +414,155 @@ "react": "^16.8 || ^17 || ^18" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + }, + "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@emotion/cache": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", + "dependencies": { + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", + "dependencies": { + "@emotion/memoize": "^0.8.1" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + }, + "node_modules/@emotion/react": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", + "dependencies": { + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + }, + "node_modules/@emotion/styled": { + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.19.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", @@ -851,6 +1002,40 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz", + "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.4.tgz", + "integrity": "sha512-0G8R+zOvQsAG1pg2Q99P21jiqxqGBW1iRe/iXHsBRBxnpXKFI8QwbB4x5KmYLggNO5m34IQgOIu9SCRfR/WWiQ==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz", + "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz", @@ -1001,6 +1186,301 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/base": { + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.16.tgz", + "integrity": "sha512-PTIbMJs5C/vYMfyJNW8ArOezh4eyHkg2pTeA7bBxh2kLP1Uzs0Nm+krXWbWGJPwTWjM8EhnDrr4aCF26+2oleg==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "5.15.16", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.16.tgz", + "integrity": "sha512-ery2hFReewko9gpDBqOr2VmXwQG9ifXofPhGzIx09/b9JqCQC/06kZXZDGGrOTpIddK9HlIf4yrS+G70jPAzUQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.16", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^18.2.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/private-theming": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", + "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.14", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", + "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.14", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", + "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", + "prop-types": "^15.8.1", + "react-is": "^18.2.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" + }, + "node_modules/@mui/x-date-pickers": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-7.3.1.tgz", + "integrity": "sha512-ZIZC+/L5ch3+J6EWRWd/rzz1z/9KPZli8sO1g7t30uFgyjpRuMK4GuazRMqDDFq1KIUtk31qJV0sKKvUsVrKRw==", + "dependencies": { + "@babel/runtime": "^7.24.0", + "@mui/base": "^5.0.0-beta.40", + "@mui/system": "^5.15.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.9.0", + "@emotion/styled": "^11.8.1", + "@mui/material": "^5.15.14", + "date-fns": "^2.25.0 || ^3.2.0", + "date-fns-jalali": "^2.13.0-0", + "dayjs": "^1.10.7", + "luxon": "^3.0.2", + "moment": "^2.29.4", + "moment-hijri": "^2.1.2", + "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "date-fns": { + "optional": true + }, + "date-fns-jalali": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + }, + "moment-hijri": { + "optional": true + }, + "moment-jalaali": { + "optional": true + } + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1330,6 +1810,11 @@ "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" + }, "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", @@ -1447,7 +1932,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -1629,6 +2113,36 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-macros/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1718,7 +2232,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -1747,7 +2260,6 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1762,11 +2274,18 @@ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -1774,8 +2293,7 @@ "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, "node_modules/combined-stream": { "version": "1.0.8", @@ -1800,6 +2318,21 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1870,6 +2403,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.11", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", + "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1980,6 +2518,14 @@ "resolved": "https://registry.npmjs.org/emoji-mart/-/emoji-mart-5.5.2.tgz", "integrity": "sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A==" }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-abstract": { "version": "1.23.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.2.tgz", @@ -2189,7 +2735,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, "engines": { "node": ">=0.8.0" } @@ -2537,6 +3082,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2638,7 +3188,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2813,7 +3362,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, "engines": { "node": ">=4" } @@ -2873,7 +3421,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2889,6 +3436,14 @@ "node": ">=12.0.0" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2902,7 +3457,6 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2977,6 +3531,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, "node_modules/is-async-function": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", @@ -3036,7 +3595,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -3358,6 +3916,11 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3419,6 +3982,11 @@ "node": ">= 0.8.0" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3471,6 +4039,11 @@ "node": ">= 18" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3704,7 +4277,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3712,6 +4284,23 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3742,14 +4331,20 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "engines": { + "node": ">=8" + } }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/possible-typed-array-names": { "version": "1.0.0", @@ -3893,6 +4488,14 @@ } } }, + "node_modules/react-circular-progressbar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/react-circular-progressbar/-/react-circular-progressbar-2.1.0.tgz", + "integrity": "sha512-xp4THTrod4aLpGy68FX/k1Q3nzrfHUjUe5v6FsdwXBl3YVMwgeXYQKDrku7n/D6qsJA9CuunarAboC2xCiKs1g==", + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3954,6 +4557,37 @@ "react-dom": ">=16.8" } }, + "node_modules/react-select": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.8.0.tgz", + "integrity": "sha512-TfjLDo58XrhP6VG5M/Mi56Us0Yt8X7xD6cDybC7yoRMUNm7BGO7qk8J0TLQOua/prb8vUOtsfnXZwfm30HGsAA==", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.1.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/react-slider": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/react-slider/-/react-slider-2.0.6.tgz", + "integrity": "sha512-gJxG1HwmuMTJ+oWIRCmVWvgwotNCbByTwRkFZC6U4MBsHqJBmxwbYRJUmxy4Tke1ef8r9jfXjgkmY/uHOCEvbA==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -4034,7 +4668,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -4242,6 +4875,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -4341,11 +4982,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -4357,7 +5002,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4375,7 +5019,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, "engines": { "node": ">=4" } @@ -4550,6 +5193,19 @@ "punycode": "^2.1.0" } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/vite": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.6.tgz", @@ -4719,6 +5375,14 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/app/package.json b/app/package.json index 74e006e..da98bd2 100644 --- a/app/package.json +++ b/app/package.json @@ -12,12 +12,17 @@ "dependencies": { "@emoji-mart/data": "^1.1.2", "@emoji-mart/react": "^1.1.1", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", "@fortawesome/fontawesome-svg-core": "^6.5.1", "@fortawesome/free-solid-svg-icons": "^6.5.1", "@fortawesome/react-fontawesome": "^0.2.0", "@iconscout/react-unicons": "^2.0.2", + "@mui/material": "^5.15.16", + "@mui/x-date-pickers": "^7.3.1", "axios": "^1.6.8", "bootstrap": "^5.3.3", + "dayjs": "^1.11.11", "dompurify": "^3.1.0", "emoji-mart": "^5.5.2", "github-markdown-css": "^5.5.1", @@ -25,8 +30,11 @@ "marked": "^12.0.1", "react": "^18.2.0", "react-bootstrap": "^2.10.2", + "react-circular-progressbar": "^2.1.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.22.3" + "react-router-dom": "^6.22.3", + "react-select": "^5.8.0", + "react-slider": "^2.0.6" }, "devDependencies": { "@types/react": "^18.2.64", diff --git a/app/public/foto1.svg b/app/public/foto1.svg new file mode 100644 index 0000000..cc5226f --- /dev/null +++ b/app/public/foto1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto2.svg b/app/public/foto2.svg new file mode 100644 index 0000000..8823fe9 --- /dev/null +++ b/app/public/foto2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto3.svg b/app/public/foto3.svg new file mode 100644 index 0000000..a204a9e --- /dev/null +++ b/app/public/foto3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto4.svg b/app/public/foto4.svg new file mode 100644 index 0000000..b388a46 --- /dev/null +++ b/app/public/foto4.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto5.svg b/app/public/foto5.svg new file mode 100644 index 0000000..223b117 --- /dev/null +++ b/app/public/foto5.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto6.svg b/app/public/foto6.svg new file mode 100644 index 0000000..faa52b4 --- /dev/null +++ b/app/public/foto6.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto7.svg b/app/public/foto7.svg new file mode 100644 index 0000000..5314106 --- /dev/null +++ b/app/public/foto7.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/foto8.svg b/app/public/foto8.svg new file mode 100644 index 0000000..9f85a21 --- /dev/null +++ b/app/public/foto8.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/app/public/logo-cinza.png b/app/public/logo-cinza.png new file mode 100644 index 0000000..efbc299 Binary files /dev/null and b/app/public/logo-cinza.png differ diff --git a/app/public/main.png b/app/public/main.png new file mode 100644 index 0000000..36cc77b Binary files /dev/null and b/app/public/main.png differ diff --git a/app/src/components/App.jsx b/app/src/components/App.jsx index efa29d8..b94d6b3 100644 --- a/app/src/components/App.jsx +++ b/app/src/components/App.jsx @@ -1,14 +1,59 @@ import Sidebar from "./app/Sidebar"; import style from "./App.module.css"; -import { Outlet } from "react-router-dom"; -import { useState } from "react"; +import { Outlet, useNavigate } from "react-router-dom"; +import { useEffect } from "react"; +import { useListaContext } from "../hooks/useListaContext"; +import { useUserContext } from "../hooks/useUserContext"; +import { useTaskContext } from "../hooks/useTaskContext"; function App() { + const { listas, dispatch: listaDispatch } = useListaContext(); + const { dispatch: taskDispatch } = useTaskContext(); + const { dispatch: userDispatch } = useUserContext(); + + const navigate = useNavigate(); + + useEffect(() => { + const userJSON = JSON.parse(localStorage.getItem("user")); + const fetchUser = async () => { + if (userJSON) { + userDispatch({ type: "SET_USER", payload: userJSON }); + } + }; + + const fetchListas = async () => { + const response = await fetch( + `http://localhost:4000/api/listas/${userJSON._id}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + const data = await response.json(); + if (response.ok) { + listaDispatch({ type: "SET_LISTAS", payload: data }); + } else { + console.log("Erro ao buscar listas"); + } + }; + + fetchUser(); + fetchListas(); + }, [listaDispatch, userDispatch]); + return ( +
+ +
+ +
+ ); } diff --git a/app/src/components/Cadastro.jsx b/app/src/components/Cadastro.jsx new file mode 100644 index 0000000..bca4882 --- /dev/null +++ b/app/src/components/Cadastro.jsx @@ -0,0 +1,275 @@ +import style from "./Cadastro.module.css"; +import { Link, useNavigate } from "react-router-dom"; +import { useState } from "react"; +import Alert from "./app/alert/Alert"; + +const Cadastro = () => { + const [nome, setNome] = useState(""); + const [email, setEmail] = useState(""); + const [senha, setSenha] = useState(""); + const [confirmarSenha, setConfirmarSenha] = useState(""); + const [iterador, setIterador] = useState(0); + const [erro, setErro] = useState(''); + const [sucesso, setSucesso] = useState(''); + + const navigate = useNavigate(); + + const images = [ + "/foto1.svg", + "/foto2.svg", + "/foto3.svg", + "/foto4.svg", + "/foto5.svg", + "/foto6.svg", + "/foto7.svg", + "/foto8.svg", + ]; + + const validarEmail = (email) => { + const regexEmail = + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/; + + if (regexEmail.test(email)) { + setEmail(email); + return true; + } else { + setErro("Email inválido."); + setTimeout(() => setErro(null), 5000); + return false; + } + }; + + const validarNome = (nome) => { + // Remover espaços em branco no início e no final do nome + if (!nome) { + return false; + } + + nome = nome.trim(); + + // Expressão regular para validar o formato do nome + const regexNome = /^[a-zA-ZáàâãéèêẽíìîĩóòôõúùûũçÇ\s-]+$/; + + // Testa se o nome é válido + if (regexNome.test(nome)) { + setNome(nome); + return true; + } else { + setErro("Nome inválido."); + setTimeout(() => setErro(null), 5000); + return false; + } + }; + + const validarSenha = (senha, tipo) => { + // Verifica se a senha é nula + if (!senha) { + return false; + } + + // Remover espaços em branco no início e no final da senha + senha = senha.trim(); + + // Testa se a senha tem pelo menos 8 caracteres + if (senha.length < 8) { + setErro("A senha deve ter pelo menos 8 caracteres."); + return false; + } + + // Testa se a senha tem pelo menos um número e uma letra + const regexSenhaNumero = /[0-9]/; + const regexSenhaLetra = /[a-zA-Z]/; + + if (regexSenhaNumero.test(senha) && regexSenhaLetra.test(senha)) { + if (tipo === "senha") { + setSenha(senha); + } else { + setConfirmarSenha(senha); + } + return true; + } else { + setErro("A senha deve conter pelo menos um número e uma letra."); + setTimeout(() => setErro(null), 5000); + return false; + } + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setErro(null); + + if (senha != confirmarSenha) { + setErro("Senha e confirmação de senha não coincidem."); + setTimeout(() => setErro(null), 5000); + return; + } + + const index = Math.floor(Math.random() * images.length); + const img = images[index]; + + try { + const info = { + nome, + email, + senha, + img, + configuracoes: { permiteEmail: true }, + }; + const response = await fetch("http://localhost:4000/api/cadastro", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(info), + }); + const data = await response.json(); + if (response.ok) { + const user = JSON.stringify(data); + localStorage.setItem("user", user); + setSucesso("Cadastro realizado com sucesso."); + setTimeout(() => setSucesso(null), 2000); + setTimeout(() => navigate("/app"), 2000); + } else { + setErro(data.error); + setTimeout(() => setErro(null), 5000); + } + } catch (error) { + console.log(error); + setTimeout(() => setErro("Erro ao cadastrar usuário."), 5000); + setErro(null); + } + }; + + const handleContinuar = () => { + switch (iterador) { + case 1: + if (validarEmail(email)) return setIterador(iterador + 1); + break; + case 2: + if (validarSenha(senha, "senha")) return setIterador(iterador + 1); + break; + default: + if (validarNome(nome)) return setIterador(iterador + 1); + } + }; + + return ( +
+ {erro && ( + + )} + {sucesso && ( + + )} +
+ + CosmicTasks + + + Já possui uma conta? Login + +
+
+
+

+ Bem-vindo ao CosmicTasks!
Vamos começar a jornada. +

+
+ + setNome(e.target.value)} + required + autoFocus + /> +
+ + + + + +
+ +
+
+ ); +}; + +export default Cadastro; diff --git a/app/src/components/Cadastro.module.css b/app/src/components/Cadastro.module.css new file mode 100644 index 0000000..70b9af3 --- /dev/null +++ b/app/src/components/Cadastro.module.css @@ -0,0 +1,151 @@ +.cadastro { + min-height: 100vh; + background-color: var(--r11); + display: flex; + flex-direction: column; + align-items: center; + position: relative; +} + +.header { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 2em; + color: var(--c1); +} + +.brand { + width: auto; + height: auto; +} + +.logo { + display: block; + width: 4em; + height: auto; +} + +.login { + font-size: 1em; + font-family: "Raleway", sans-serif; + font-weight: 400; +} + +.login a { + font-weight: 600; + color: var(--a4); +} + +.wrapper { + display: flex; + flex-direction: column; + gap: 2em; + padding: 2em; + align-items: center; + width: 700px; + max-width: 100%; + position: relative; +} + +.form { + display: flex; + flex-direction: column; + gap: 1em; + padding: 2em; + border-radius: 1em; + max-width: 400px; + width: 100%; + + /* From https://css.glass */ + background: rgba(32, 0, 95, 0.01); + border-radius: 16px; + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.3); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.text { + color: var(--c3); + padding: 0; + margin: 0; + line-height: 1.5; +} + +.inputGroup { + position: relative; +} + +.label { + font-size: 1em; + font-weight: 600; + margin: 0; + color: var(--c3); +} + +.input { + background-color: var(--r11) !important; + border: 1px solid var(--c6); + border-radius: 0.5em; + padding: 0.5em; + color: var(--c1); + font-size: 1em; + font-weight: 400; + width: 100%; +} +.input:focus { + outline: none; + border: 1px solid var(--c3); +} + +.input::placeholder { + color: var(--c6); + font-weight: normal; +} + +.eye { + position: absolute; + bottom: 0.5em; + right: 0.5em; + cursor: pointer; +} + +.continuar { + background-color: transparent; + color: var(--c3); + border: 1px solid var(--c3); + border-radius: 0.5em; + padding: 0.5em; + font-size: 1em; + font-weight: 600; +} + +.cadastrar { + background-color: var(--a4); + color: var(--a12); + border: none; + border-radius: 0.5em; + padding: 0.5em; + font-size: 1em; + font-weight: 600; +} + +.cadastrar:hover { + background-color: var(--a3); +} + +.footer { + width: fit-content; + text-wrap: wrap; + text-align: center; + font-size: 1em; + color: var(--c5); +} + +@media (max-width: 400px) { + .form { + padding: 1em; + } +} diff --git a/app/src/components/Login.jsx b/app/src/components/Login.jsx new file mode 100644 index 0000000..9741263 --- /dev/null +++ b/app/src/components/Login.jsx @@ -0,0 +1,115 @@ +import { useState } from "react"; +import style from "./Login.module.css"; +import { Link, useNavigate } from "react-router-dom"; +import Alert from "./app/alert/Alert"; + +const Login = () => { + const [email, setEmail] = useState(""); + const [senha, setSenha] = useState(""); + const [erro, setErro] = useState(null); + const [sucesso, setSucesso] = useState(false); + + const navigate = useNavigate(); + + const handleLogin = async (e) => { + e.preventDefault(); + setErro(null); + try { + const info = { email, senha }; + const response = await fetch("http://localhost:4000/api/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(info), + }); + const data = await response.json(); + if (response.ok) { + const user = JSON.stringify(data); + localStorage.setItem("user", user); + setSucesso(true); + setTimeout(() => { + setSucesso(null); + navigate("/app"); + }, 2000); + } else { + setErro(data.error); + setTimeout(() => setErro(null), 5000); + } + } catch (error) { + console.error(error); + setTimeout(() => setErro("Erro ao realizar login."), 5000); + setErro(null); + } + }; + + return ( +
+
+ {erro && ( + + )} + {sucesso && ( + + )} +
+ CosmicTasks +

Entrar no CosmicTasks

+
+
+
+ + setEmail(e.target.value)} + autoFocus + required + /> +
+
+ + setSenha(e.target.value)} + required + minLength={8} + /> +
+ +
+
+

+ Não tem uma conta?{" "} + + Cadastre-se + +

+
+ +
+
+ ); +}; + +export default Login; diff --git a/app/src/components/Login.module.css b/app/src/components/Login.module.css new file mode 100644 index 0000000..e8754d1 --- /dev/null +++ b/app/src/components/Login.module.css @@ -0,0 +1,135 @@ +.login { + min-height: 100vh; + background-color: var(--c1); + display: flex; + justify-content: center; +} + +.container { + display: flex; + flex-direction: column; + gap: 2em; + padding: 2em; + align-items: center; + width: 700px; + max-width: 100%; + position: relative; +} + +.header { + display: flex; + flex-direction: column; + gap: 0.5em; + align-items: center; +} + +.logo { + display: block; + width: 5em; + height: auto; +} + +.chamada { + font-family: "Raleway", sans-serif; + font-size: 1.5em; + margin: 0; + width: fit-content; + color: var(--c12); +} + +.form { + display: flex; + flex-direction: column; + gap: 1em; + padding: 2em; + border-radius: 1em; + max-width: 400px; + width: 100%; + /* From https://css.glass */ + background: rgba(81, 26, 190, 0.03); + border-radius: 16px; + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1); + border: 1px solid rgba(81, 26, 190, 0.1); +} + +.inputGroup { + display: flex; + flex-direction: column; + gap: 0.5em; +} + +.label { + font-size: 1em; + font-weight: 600; + margin: 0; + color: var(--c10); +} + +.input { + background-color: var(--c1) !important; + border: 1px solid var(--c3); + border-radius: 0.5em; + padding: 0.5em; + color: var(--c10); + font-size: 1em; + font-weight: 400; + width: 100%; +} + +.input:focus { + outline: none; + border: 1px solid var(--c9); +} + +.input::placeholder { + color: var(--c5); + font-weight: normal; +} + +.button { + background-color: var(--r7); + color: var(--r1); + border: none; + border-radius: 0.5em; + padding: 0.5em; + font-size: 1em; + font-weight: 600; +} + +.button:hover { + background-color: var(--r8); + cursor: pointer; +} + +.createAccount { + width: fit-content; +} + +.createAccount > p { + font-size: 1em; + font-weight: 400; + margin: 0; +} + +.createAccount > p > a { + color: var(--r7); + text-decoration: none; +} + +.createAccount > p > a:hover { + text-decoration: underline; +} + +.footer { + width: fit-content; + text-wrap: wrap; + text-align: center; + font-size: 1em; + color: var(--c9); +} + +@media (max-width: 400px) { + .form { + padding: 1em; + } +} diff --git a/app/src/components/LoginPage.jsx b/app/src/components/LoginPage.jsx index 155c343..c0dcb13 100644 --- a/app/src/components/LoginPage.jsx +++ b/app/src/components/LoginPage.jsx @@ -5,28 +5,106 @@ import PropTypes from "prop-types"; import { Link } from "react-router-dom"; const LoginPage = ({ acao }) => { + const [nome, setNome] = useState(""); const [email, setEmail] = useState(""); const [senha, setSenha] = useState(""); + const images = [ + '/foto1.svg', + '/foto2.svg', + '/foto3.svg', + '/foto4.svg', + '/foto5.svg', + '/foto6.svg', + '/foto7.svg', + '/foto8.svg' + ]; + const [erro, setErro] = useState(""); + const [disabled, setDisabled] = useState(style.disabled); + + const handleChange = (e) => { + if (acao === "login") { + if (e.target.validity.valid && senha.length >= 8) { + setDisabled(""); + } else { + setDisabled(style.disabled); + } + } + if (acao === "cadastro") { + if (e.target.validity.valid && senha.length >= 8 && nome.length > 0) { + setDisabled(""); + } else { + setDisabled(style.disabled); + } + } + }; const handleLogin = async () => { + setErro(""); try { - const user = { email, senha }; + const info = { email, senha }; const response = await fetch("http://localhost:4000/api/login", { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify(user), + body: JSON.stringify(info), }); - response.ok ? window.location.replace("/app") : alert("Erro ao logar"); + const data = await response.json(); + if (response.ok) { + const user = JSON.stringify(data); + localStorage.setItem("user", user); + window.location.replace("/app"); + } else { + setErro(response.error); + } } catch (error) { console.error(error); + alert("Erro ao fazer login."); + } + }; + + const handleCadastro = async () => { + const index = Math.floor(Math.random() * images.length); + const img = images[index]; + setErro(false); + try { + const info = { + nome, + email, + senha, + img, + configuracoes: { permiteEmail: true }, + }; + const response = await fetch("http://localhost:4000/api/cadastro", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(info), + }); + const data = await response.json(); + if (response.ok) { + const user = JSON.stringify(data); + console.log('Usuario criado: ', user); + localStorage.setItem("user", user); + window.location.replace("/app"); + } else { + setErro(data.error || response.statusText || response.status.toString); + } + } catch (error) { + console.error(error); + alert("Erro ao cadastrar usuário."); } }; const handleSubmit = (e) => { e.preventDefault(); - handleLogin(); + if (acao === "login") { + handleLogin(); + } + if (acao === "cadastro") { + handleCadastro(); + } }; const login = ( @@ -46,11 +124,7 @@ const LoginPage = ({ acao }) => {

Entrar

Bem-vindo de volta!

-
handleSubmit(e)} - > + handleSubmit(e)}>
@@ -76,13 +153,18 @@ const LoginPage = ({ acao }) => { className={style.input} placeholder="Insira sua senha" value={senha} - onChange={(e) => setSenha(e.target.value)} + onChange={(e) => { + setSenha(e.target.value); + }} + minLength="8" />
- - Esqueceu a senha? - -

@@ -101,7 +183,21 @@ const LoginPage = ({ acao }) => {

Cadastrar

Comece sua jornada!

- + handleSubmit(e)}> +
+ + setNome(e.target.value)} + minLength="1" + /> +
@@ -124,13 +221,16 @@ const LoginPage = ({ acao }) => { name="password" className={style.input} placeholder="Crie uma senha forte" + onChange={(e) => setSenha(e.target.value)} + minLength="8" />
-

diff --git a/app/src/components/LoginPage.module.css b/app/src/components/LoginPage.module.css index 95f4e04..92e6515 100644 --- a/app/src/components/LoginPage.module.css +++ b/app/src/components/LoginPage.module.css @@ -93,6 +93,16 @@ } } +.error { + color: var(--vermelho); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 400; + width: fit-content; + margin: 0; +} + .forgotPassword { color: var(--r7); font-family: Raleway; @@ -109,6 +119,8 @@ font-style: normal; font-weight: 400; border-radius: 8px; + background: var(--r7); + color: var(--c1); &.disabled { background: var(--c7); diff --git a/app/src/components/app/Listas.jsx b/app/src/components/app/Listas.jsx deleted file mode 100644 index 5efd538..0000000 --- a/app/src/components/app/Listas.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useState } from 'react' - -export default function useListas() { - const [listas, setListas] = useState([ - { - id: 1, - nome: "Francês", - emoji: "croissant", - cor: null, - tarefas: [ - { - id: 1, - nome: "Estudar francês", - data: "26/03", - concluido: false, - }, - { - id: 2, - nome: "Praticar francês", - data: "26/03", - concluido: false, - }, - ], - }, - { - id: 2, - nome: "Inglês", - emoji: null, - cor: "var(--azul-royal)", - tarefas: [ - { - id: 1, - nome: "Estudar inglês", - data: "26/03", - concluido: false, - }, - { - id: 2, - nome: "Praticar inglês", - data: "26/03", - concluido: false, - }, - ], - }, - ]); - - const addLista = (nome, decoracao, modo) => { - let novaLista = []; - if (modo === "emoji") { - novaLista = [ - ...listas, - { - id: listas.length + 1, - nome: nome, - emoji: decoracao, - cor: null, - tarefas: [], - }, - ]; - } else { - novaLista = [ - ...listas, - { - id: listas.length + 1, - nome: nome, - emoji: null, - cor: decoracao, - tarefas: [], - }, - ]; - } - - setListas(novaLista); - }; - - return { listas, setListas, addLista }; -} \ No newline at end of file diff --git a/app/src/components/app/PageCard.module.css b/app/src/components/app/PageCard.module.css index 9fedc32..2e8e843 100644 --- a/app/src/components/app/PageCard.module.css +++ b/app/src/components/app/PageCard.module.css @@ -249,6 +249,421 @@ border: 1px solid var(--r12); } +.tarefaTitle { + color: var(--r12); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.deckTitle { + width: 90%; + + color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 15px; + font-style: normal; + font-weight: 500; + line-height: normal; + margin: 0; + + +} + +.add-cards-button { + font-size: 16px; + cursor: pointer; + font-weight: bold; + color: #3751ad; + width: 30%; +} + +.editDeck { + width: 90%; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 24px; +} + +.editDeckName{ + width: 90%; +} + +.remove-deck-button { + cursor: pointer; +} + +.view-deck-button { + cursor: pointer; +} + +.deck-buttons { + width: 90%; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.wrapperNiveis { + display: flex; + justify-content: center; + margin-top: 20px; +} + +.nivel { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 0 8px; +} + +.nivel span { + color: var(--c12); + font-family: Lato; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + + +.secHeader { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 8px; +} + +.cardSecTitle { + color: var(--c12); + font-family: 'Raleway',sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + margin-left: 80px; +} + +.qtdCards { + font-family: Lato; + font-weight: 700; +} + +.addCard { + padding: 8px 16px; + border-radius: 8px; + color: var(--r1); + background-color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + border: #F2E5FF; + margin-right: 15px; +} + +.wrapperEstudar { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 8px; + width: 100%; + align-items: center; +} + +.qtdEstudar { + color: var(--r12); + font-family: Lato; + font-size: 32px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.btnEstudar { + padding: 8px 16px; + border-radius: 8px; + background-color: var(--r7); + color: var(--r1); + font-family: 'Raleway',sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + border: none; +} + +.saveDeck { + background-color: var(--r7); + color: var(--r1); + border: none; + border-radius: 8px; +} + +.selectedDeck { + background-color: #07001B; +} + +.caixasBody { + padding: 16px; + display: flex; + gap: 16px; + flex-wrap: wrap; + width: 100%; + box-shadow: 1px 0px 0px 0px var(--c3); +} + +.caixa { + display: flex; + width: 224px; + height: fit-content; + padding: 16px; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 8px; + + border-radius: 8px; + border: 1px solid var(--c7); + + &.active { + border-top: 1px solid var(--r7); + border-right: 2px solid var(--r7); + border-bottom: 2px solid var(--r7); + border-left: 1px solid var(--r7); + } +} + +.tituloCaixa { + color: var(--r12); + font-family: Raleway; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; + margin: 0; +} + +.descCaixa { + color: var(--c10); + font-family: Lato; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.listaCaixas { + display: flex; + align-items: center; + gap: 8px; + list-style: none; + margin: 0; + padding: 0; +} + +.noCardSelected { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 16px; + padding: 16px; + width: 36%; + min-width: 320px; +} + +.img { + width: 100px; + height: 100px; +} + +.infoText { + color: var(--c7); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.cardInfo { + display: flex; + flex-direction: column; + + align-items: center; + width: 36%; + min-width: 320px; + padding: 16px; + height: 500px; + display: inline-block; +} + +.cardHeader { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 8px 16px; +} + +.cardTitle { + color: var(--c12); + font-family: Raleway; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.wrapperNiveis { + display: flex; + justify-content: center; +} + +.nivel { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 0 8px; +} + +.nivel span { + color: var(--c12); + font-family: Lato; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.secHeader { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 8px; +} + +.cardSecTitle { + color: var(--c12); + font-family: Raleway; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.qtdCards { + font-family: Lato; + font-weight: 700; +} + +.addCard { + padding: 8px 16px; + border-radius: 8px; + color: var(--r1); + background-color: var(--r12); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.wrapperEstudar { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 8px; + width: 100%; + align-items: center; +} + +.qtdEstudar { + color: var(--r12); + font-family: Lato; + font-size: 32px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.btnEstudar { + padding: 8px 16px; + border-radius: 8px; + background-color: var(--r7); + color: var(--r1); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.wrapperTabs { + display: flex; + flex-direction: column; + gap: 10px; + align-items: flex-start; + padding: 8px; + background-color: var(--r1); + border-radius: 8px; + width: 100%; +} + +.nav { + display: flex; + background-color: var(--r12); + border-radius: 8px; + padding: 4px; +} + +.tab { + color: var(--r1); + font-family: Lato; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + background-color: transparent; + border-radius: 4px; + padding: 4px 8px; +} + +.tab.active { + background-color: var(--r1); + color: var(--r12); +} + +.tarefas { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; +} + +.tarefa { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + padding: 16px; + border-radius: 8px; + background-color: transparent; + width: 100%; + border: 1px solid var(--r12); +} + .tarefaTitle { color: var(--r12); font-family: Raleway; diff --git a/app/src/components/app/PageCards.jsx b/app/src/components/app/PageCards.jsx index 7da9b8f..2cf1fea 100644 --- a/app/src/components/app/PageCards.jsx +++ b/app/src/components/app/PageCards.jsx @@ -2,115 +2,22 @@ import React, { useState } from "react"; import style from "./PageCard.module.css"; import { UilBox, UilEdit } from "@iconscout/react-unicons"; import logo from "../../assets/logo-cinza.png"; +import Deck from "./flashcards/components/Deck"; +import { DeckContext } from "./flashcards/components/contexts/DeckContext"; +import { DeckProvider } from "./flashcards/components/contexts/DeckContext"; function PageCard() { const [selectedCard, setSelectedCard] = useState(1); return ( <> -

-
-

Vocabulário Francês

- 2 cartões, 2 para hoje -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
-

Vocabulário Francês

- 2 cartões, 2 para hoje -
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
-
-
- {selectedCard ? ( -
-
-

Vocabulário Francês

-
-
-
- 2 - -
-
- 0 - -
-
- 0 - -
-
- 0 - -
-
- 0 - -
-
-
-

- Cartões na caixa: 2 -

- -
-
-

0/2

- -
-
-
- - - -
-
-
- Vouloir - -
-
-
-
- ) : ( -
- Logo -

- Clique em uma caixa para ver mais informações -

-
- )} + + + + + + + ); } diff --git a/app/src/components/app/PageTasks.jsx b/app/src/components/app/PageTasks.jsx index 3ccf854..2e55908 100644 --- a/app/src/components/app/PageTasks.jsx +++ b/app/src/components/app/PageTasks.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import { useState, useEffect } from "react"; import SecSidebar from "./SecSidebar"; import style from "./PageTasks.module.css"; import { @@ -8,60 +8,171 @@ import { UilGrid, UilAngleDown, UilCircle, + UilInbox, } from "@iconscout/react-unicons"; -import useListas from "./Listas"; +import { useListaContext } from "../../hooks/useListaContext"; +import { useTaskContext } from "../../hooks/useTaskContext"; +import Tarefa from "./tarefas/Tarefa"; +import ContainerTask from "./tarefas/ContainerTask"; +import { useParams } from "react-router-dom"; -const PageTasks = () => { +const PageTasks = ({ tipo }) => { const [sidebar, setSidebar] = useState(true); - const { listas, setListas, addLista } = useListas(); + const [tarefaSelecionada, setTarefaSelecionada] = useState(null); + const { listas, dispatch: dispatchListas } = useListaContext(); + const { dispatch: dispatchTasks } = useTaskContext(); + const { idLista } = useParams(); + const [lista, setLista] = useState(null); + const [tipoNome, setTipoNome] = useState(null); + + const concluirTarefa = async (id) => { + const response = await fetch(`http://localhost:4000/api/tasks/${id}`, { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + status: "Concluída", + }), + }); + if (response.ok) { + const data = await response.json(); + dispatchTasks({ type: "REMOVE_TASK", payload: data }); + } else { + console.log("Erro ao concluir tarefa"); + } + } + + useEffect(() => { + const user = JSON.parse(localStorage.getItem("user")); + + const fetchTasks = async () => { + const response = await fetch( + `http://localhost:4000/api/tasks/${user._id}?list=${tipo}&idLista=${idLista} `, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + const data = await response.json(); + if (response.ok) { + if (listas) { + const tasksNomeLista = data.map((task) => { + const lista = listas.find((lista) => lista._id === task.lista); + return { + ...task, + nomeLista: lista.nome, + corLista: lista.cor, + emojiLista: lista.emoji, + }; + }); + dispatchTasks({ type: "SET_TASKS", payload: tasksNomeLista }); + } else { + console.log("Erro ao buscar listas no fetchTasks de PageTasks"); + } + } else { + console.log("Erro ao buscar tarefas"); + } + }; + + const fetchLista = async () => { + const response = await fetch( + `http://localhost:4000/api/listas/unica/${idLista}`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + } + ); + const data = await response.json(); + if (response.ok) { + setLista(data); + } else { + console.log("Erro ao buscar listas"); + } + }; + + fetchTasks(); + if (idLista) { + fetchLista(); + } + + switch (tipo) { + case "hoje": + setTipoNome("Hoje"); + break; + case "entrada": + setTipoNome("Entrada"); + break; + case "concluidas": + setTipoNome("Concluídas"); + break; + case "atrasadas": + setTipoNome("Atrasadas"); + break; + case "excluidas": + setTipoNome("Excluídas"); + break; + default: + setTipoNome(null); + break; + } + }, [tipo, dispatchTasks, listas, idLista, setLista]); + + const { tasks } = useTaskContext(); return ( <> - +
{sidebar ? ( setSidebar(false)} /> ) : ( setSidebar(true)} /> )} -

Hoje

+

+ {tipoNome ? tipoNome : lista ? lista.nome : "Tarefas"} +

- - + +
- {listas.map((lista) => ( -
-
- - {lista.nome} -
- {lista.tarefas.map((tarefa) => ( -
-
- - {tarefa.nome} -
-
- {lista.nome} - {tarefa.data} -
-
+
+ {tasks && + tasks.map((task) => ( + ))} -
- ))} +
+ {tarefaSelecionada && ( + + )} ); }; diff --git a/app/src/components/app/PageTasks.module.css b/app/src/components/app/PageTasks.module.css index c1ab87c..ee71d8e 100644 --- a/app/src/components/app/PageTasks.module.css +++ b/app/src/components/app/PageTasks.module.css @@ -1,26 +1,28 @@ .tasksBody { display: flex; flex-direction: column; - gap: 8px; - padding: 16px; + gap: 0.5rem; + padding: 1rem; width: 100%; + box-shadow: 1px 0px 0px 0px var(--c3); } .tasksHeader { display: flex; - padding: 16px 0; + padding: 0.5em 0; justify-content: space-between; align-items: center; width: 100%; - gap: 16px; + gap: 1rem; } .switchSidebarContainer { display: flex; align-items: center; - gap: 16px; + gap: 1rem; & > svg { + fill: var(--c12); cursor: pointer; } } @@ -28,100 +30,84 @@ .listTitle { color: var(--c11); font-family: Raleway; - font-size: 20px; + font-size: 1.5rem; font-style: normal; font-weight: 700; line-height: normal; margin: 0; display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; } .gridSettings { display: flex; - gap: 16px; + gap: 1rem; align-items: center; & > * { + fill: var(--c12); cursor: pointer; } } -.lista { +.taskWrapper { display: flex; flex-direction: column; - gap: 8px; - border-radius: 8px; -} - -.listaHeader { - display: flex; - align-items: center; - gap: 4px; - padding: 4px 0; - cursor: pointer; -} - -.listaNome { - color: var(--c11); - font-family: Raleway; - font-size: 12px; - font-style: normal; - font-weight: 700; - line-height: normal; + gap: 0.5rem; + width: 100%; } -.tarefa { +.task { display: flex; align-items: center; justify-content: space-between; - padding: 8px; - gap: 16px; - border-radius: 8px; - border: 1px solid var(--c7); + padding: 0.5rem; + gap: 1rem; + border-radius: 0.5rem; + border: 1px solid var(--c12); &:hover { background-color: var(--r1); } } -.tarefaCheck { +.taskCheck { display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; } -.tarefaNome { +.taskNome { color: var(--c12); font-family: Raleway; - font-size: 12px; + font-size: 1rem; font-style: normal; font-weight: 500; line-height: normal; } -.tarefaOptions { +.taskOptions { display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; flex-wrap: wrap; } -.tarefaOptionsLista { +.taskOptionsLista { color: var(--c10); font-family: Raleway; - font-size: 10px; + font-size: 0.8rem; font-style: normal; font-weight: 400; line-height: normal; } -.tarefaOptionsData { +.taskOptionsData { color: var(--azul-royal); font-variant-numeric: lining-nums proportional-nums; font-family: Raleway; - font-size: 12px; + font-size: 0.8rem; font-style: normal; font-weight: 400; line-height: normal; diff --git a/app/src/components/app/SecSidebar.jsx b/app/src/components/app/SecSidebar.jsx index 3146f91..e873e84 100644 --- a/app/src/components/app/SecSidebar.jsx +++ b/app/src/components/app/SecSidebar.jsx @@ -1,113 +1,129 @@ -import { useState } from "react"; +import { useState} from "react"; +import PropTypes from "prop-types"; import style from "./SecSidebar.module.css"; import { UilPlus, UilSun, UilInbox, - UilCalendarAlt, - UilPuzzlePiece, - UilAngleDown, - UilTagAlt, UilCheckCircle, UilExclamationCircle, UilTrashAlt, - UilSquareShape, } from "@iconscout/react-unicons"; import ModalNewList from "./modal/ModalNewList"; +import ModalNewTask from "./modal/ModalNewTask"; +import BtnListas from "./listas/BtnListas"; import data from "@emoji-mart/data"; import { init } from "emoji-mart"; init({ data }); +import { useListaContext } from "../../hooks/useListaContext"; +import { NavLink } from "react-router-dom"; -const SecSidebar = ({isOpen, listas, addLista}) => { - - const [showModal, setShowModal] = useState(false); +const SecSidebar = ({ isOpen }) => { + const { listas } = useListaContext(); + + const [showModalNewList, setShowModalNewList] = useState(false); + const [showModalNewTask, setShowModalNewTask] = useState(false); const handleAddLista = () => { - setShowModal(!showModal); + setShowModalNewTask(false); + setShowModalNewList(!showModalNewList); + }; + + const handleAddTask = () => { + setShowModalNewList(false); + setShowModalNewTask(!showModalNewTask); }; return (
- - - - - -
-
-
-
-
- Listas -
- -
- {listas.map((lista, id) => ( - - ))} -
- -
-
-
- Categorias -
- -
- -
-
-
- - - +
- {showModal && ( - +
+ {listas && listas.length != 0 ? ( + <> +
+
+
Listas
+ +
+ {listas.map((lista) => ( + + ))} +
+
+ + ) : ( + + )} + + {showModalNewList && ( + + )} + {showModalNewTask && ( + )}
); }; +SecSidebar.propTypes = { + isOpen: PropTypes.bool.isRequired, +}; + export default SecSidebar; diff --git a/app/src/components/app/SecSidebar.module.css b/app/src/components/app/SecSidebar.module.css index dbadc6c..c950551 100644 --- a/app/src/components/app/SecSidebar.module.css +++ b/app/src/components/app/SecSidebar.module.css @@ -4,13 +4,13 @@ display: flex; } 100% { - width: 240px; + width: 15rem; } } @keyframes close { 0% { - width: 240px; + width: 15rem; } 100% { width: 0; @@ -21,66 +21,111 @@ .secSidebar { display: flex; flex-direction: column; - width: 240px; + min-width: 15rem; height: 100vh; - padding: 16px 8px; + padding: 1rem 0.5rem; box-shadow: 1px 0px 0px 0px var(--c3); user-select: none; - gap: 12px; + gap: 0.75rem; overflow: scroll; scrollbar-width: none; overflow-x: hidden; +} - &.open { - animation: open 0.5s forwards; - } - &.close { - animation: close 0.5s forwards; - } +.secSidebar.open { + animation: open 0.5s forwards; +} + +.secSidebar.close { + animation: close 0.5s forwards; } .mainItems, .secItems { display: flex; flex-direction: column; - gap: 8px; + gap: 0.5rem; } .item { display: flex; - gap: 8px; - padding: 8px; + gap: 0.5rem; + padding: 0.5rem; align-items: center; - border-radius: 8px; + border-radius: 0.5rem; background-color: transparent; width: 100%; position: relative; + color: var(--c12); +} - .listName { - color: var(--c11); - font-family: Raleway; - font-size: 12px; - font-style: normal; - font-weight: 500; - line-height: normal; - } +.item.add { + background-color: transparent; + border: none; + outline: none; + color: var(--r7); +} - .badge { - color: var(--c7); - font-family: Raleway; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - position: absolute; - right: 8px; - top: auto; - bottom: auto; +.item.add:hover { + background-color: var(--r7); + color: var(--c1); + + svg { + fill: var(--c1); } } +.item.add > svg { + fill: var(--r7); +} + +.item > svg { + fill: var(--c12); +} + +.item .listName { + font-family: Raleway; + font-size: 1rem; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.item:hover .badge { + display: flex; +} + +.item.active { + background-color: var(--r7); + color: var(--c1); +} + +.item.active > svg { + fill: var(--c1); +} + +.item .badge { + color: var(--c7); + font-family: Raleway; + font-size: 0.5rem; + font-style: normal; + font-weight: 400; + line-height: normal; + position: absolute; + display: none; + align-items: center; + gap: 0.25rem; + right: 0.5rem; + top: auto; + bottom: auto; +} + .item:hover { - background-color: var(--r1); + background-color: var(--r7); + color: var(--c1); + svg { + fill: var(--c1); + } } .divider { @@ -96,23 +141,23 @@ .categorias { display: flex; flex-direction: column; - gap: 8px; + gap: 0.5rem; } .headerItems { color: var(--c8); font-family: Raleway; - font-size: 12px; + font-size: 0.75rem; font-style: normal; font-weight: 700; line-height: normal; display: flex; justify-content: space-between; - gap: 8px; + gap: 0.5rem; +} - .add { - cursor: pointer; - } +.headerItems .add { + cursor: pointer; } .accordion { diff --git a/app/src/components/app/Sidebar.jsx b/app/src/components/app/Sidebar.jsx index a770b85..5b2ef2e 100644 --- a/app/src/components/app/Sidebar.jsx +++ b/app/src/components/app/Sidebar.jsx @@ -12,19 +12,25 @@ import { UilSetting, } from "@iconscout/react-unicons"; import { NavLink } from "react-router-dom"; +import { useUserContext } from "../../hooks/useUserContext"; const Sidebar = () => { + + const userJSON = JSON.parse(localStorage.getItem("user")); + + const img = userJSON.img ? userJSON.img : ViteLogo; + return (
- Perfil do usuário + Perfil do usuário
- + {({ isActive }) => ( )} @@ -33,37 +39,37 @@ const Sidebar = () => {
{({ isActive }) => ( - + )}
{({ isActive }) => ( - + )}
{({ isActive }) => ( - + )}
- +
- +
- +
- +
diff --git a/app/src/components/app/Sidebar.module.css b/app/src/components/app/Sidebar.module.css index a01fb1c..68e4110 100644 --- a/app/src/components/app/Sidebar.module.css +++ b/app/src/components/app/Sidebar.module.css @@ -4,7 +4,7 @@ justify-content: space-between; align-items: center; height: 100vh; - padding: 16px 8px; + padding: 1rem 0.5rem; width: fit-content; background-color: var(--r7); box-shadow: 1px 0px 0px 0px var(--c3); @@ -13,13 +13,13 @@ .mainItems { display: flex; flex-direction: column; - gap: 16px; + gap: 1rem; align-items: center; } .img { - width: 32px; - height: 32px; + width: 2rem; + height: 2rem; border-radius: 50%; } @@ -30,14 +30,14 @@ .secondaryItems { display: flex; flex-direction: column; - gap: 16px; + gap: 1rem; align-items: center; } .link svg { - fill: var(--r1); + fill: var(--c1); } .link svg.active { - fill: var(--a3); + fill: var(--a4); } diff --git a/app/src/components/app/alert/Alert.jsx b/app/src/components/app/alert/Alert.jsx new file mode 100644 index 0000000..a40570b --- /dev/null +++ b/app/src/components/app/alert/Alert.jsx @@ -0,0 +1,35 @@ +import { UilExclamationTriangle, UilCheck } from "@iconscout/react-unicons"; +import { useEffect, useState } from "react"; +import style from "./Alert.module.css"; + +const Alert = ({ tipo, conteudo }) => { + const [icone, setIcone] = useState(null); + const [classList, setClassList] = useState([style.alert]); + + useEffect(() => { + switch (tipo) { + case "erro": + setIcone(); + setClassList([...classList, style.erro]); + break; + case "sucesso": + setIcone(); + setClassList([...classList, style.sucesso]); + break; + default: + setClassList([...classList, style.erro]); + setIcone(); + } + }, [tipo]); + + return ( +
+
+ {icone} + {conteudo} +
+
+ ); +}; + +export default Alert; diff --git a/app/src/components/app/alert/Alert.module.css b/app/src/components/app/alert/Alert.module.css new file mode 100644 index 0000000..666cb1f --- /dev/null +++ b/app/src/components/app/alert/Alert.module.css @@ -0,0 +1,27 @@ +.wrapper { + position: absolute; + width: 100%; + padding: 2em; + max-width: 700px; +} + +.alert { + width: 100%; + display: flex; + align-items: center; + gap: 0.5em; + border-radius: 0.5em; + padding: 0.5em; + font-weight: 600; + cursor: pointer; +} + +.alert.erro { + background-color: var(--vermelho); + color: var(--c1); +} + +.alert.sucesso { + background-color: var(--verde-floresta); + color: var(--c1); +} \ No newline at end of file diff --git a/app/src/components/app/flashcards/components.rar b/app/src/components/app/flashcards/components.rar new file mode 100644 index 0000000..0aa78e1 Binary files /dev/null and b/app/src/components/app/flashcards/components.rar differ diff --git a/app/src/components/app/flashcards/components.zip b/app/src/components/app/flashcards/components.zip new file mode 100644 index 0000000..b9e1504 Binary files /dev/null and b/app/src/components/app/flashcards/components.zip differ diff --git a/app/src/components/app/flashcards/components/Deck.jsx b/app/src/components/app/flashcards/components/Deck.jsx new file mode 100644 index 0000000..ba7c66e --- /dev/null +++ b/app/src/components/app/flashcards/components/Deck.jsx @@ -0,0 +1,179 @@ +import React, { useContext, useState, useEffect } from 'react'; +import { DeckContext } from './contexts/DeckContext'; +import FlashcardForm from './FlashCardForm'; +import Modal from 'react-modal'; +import style from './Deck.module.css'; +import { UilBox, UilEdit, UilPlus } from '@iconscout/react-unicons'; +import StudyFlashcardsModal from './StudyModal'; +import DeckForm from './DeckForm' + +// Definindo estilos customizados para o modal +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + width: '400px', // Largura do modal + height: '300px', // Altura do modal + padding: '20px', // Padding interno + backgroundColor: '#f0f0f0', // Cor de fundo do modal + borderRadius: '10px', // Bordas arredondadas + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', // Sombra + }, + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0.5)', // Fundo semitransparente + }, +}; + +const Deck = () => { + const { + decks, + selectedDeck, + flashcardToEdit, + isModalOpen, + isCreatingDeck, + openModal, + closeModal, + saveFlashcard, + saveDeck, + selectDeck, + setIsCreatingDeck, + totalcards, + deck, + flashcards, + } = useContext(DeckContext); + + const [isStudyModalOpen, setIsStudyModalOpen] = useState(false); + const [currentCardIndex, setCurrentCardIndex] = useState(0); + const [showAnswer, setShowAnswer] = useState(false); + + const handleStudyClick = () => { + setIsStudyModalOpen(true); + }; + + const closeStudyModal = () => { + setIsStudyModalOpen(false); + setCurrentCardIndex(0); + setShowAnswer(false); + }; + + const handleNextCard = () => { + setCurrentCardIndex((prevIndex) => (prevIndex + 1) % selectedDeck.flashcards.length); + setShowAnswer(false); + }; + + const handleShowAnswer = () => { + setShowAnswer(true); + }; + + // Funções para filtrar os flashcards + const filterFlashcards = () => { + if (!selectedDeck) return []; + + switch (activeTab) { + case 'hoje': + return selectedDeck.flashcards.filter(flashcard => flashcard.dueDate === new Date().toISOString().split('T')[0]); + case 'atuais': + return selectedDeck.flashcards.filter(flashcard => flashcard.status === 'current'); + case 'concluidas': + return selectedDeck.flashcards.filter(flashcard => flashcard.status === 'completed'); + default: + return selectedDeck.flashcards; + } + }; + + return ( +
+ + {decks.map((deck) => ( +
+
+

selectDeck(deck.id)}> + {deck.name} +

+ cartões, 2 para hoje + +
+ {deck.flashcards.map((flashcard) => ( +
+ {flashcard.deckId === deck.id &&

{flashcard.question}

} +
+ ))} +
+ ))} + +
+
+
+ { + setIsCreatingDeck(true); + openModal(null); + }} + > +
+
+
+ +
+ +
+ + {selectedDeck && ( +
+
+

{selectedDeck.name}

+
+ +
+

Total de cartões: {selectedDeck.flashcards.length}

+ +
+
+ + +
+ + + + + + + + {isStudyModalOpen && selectedDeck && ( + + )} +
+ )} + + + {isCreatingDeck ? ( + { + saveDeck(deck); + closeModal(); + }} + /> + ) : ( + + )} + +
+ ); +}; + +export default Deck; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/Deck.module.css b/app/src/components/app/flashcards/components/Deck.module.css new file mode 100644 index 0000000..c55de84 --- /dev/null +++ b/app/src/components/app/flashcards/components/Deck.module.css @@ -0,0 +1,453 @@ + + +@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@100&display=swap"); + + +:root { + + --r12:#07001B; + --r1:#F2E5FF; + --r7:#511ABE; + --c6:#8B888F; + --c1:#F3EDFA +} + +.deckTitle { + width: 90%; + + color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 15px; + font-style: normal; + font-weight: 500; + line-height: normal; + margin: 0; + + +} + +.add-cards-button { + font-size: 16px; + cursor: pointer; + font-weight: bold; + color: #3751ad; + width: 30%; +} + +.editDeck { + width: 90%; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 24px; +} + +.editDeckName{ + width: 90%; +} + +.remove-deck-button { + cursor: pointer; +} + +.view-deck-button { + cursor: pointer; +} + +.deck-buttons { + width: 90%; + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.wrapperNiveis { + display: flex; + justify-content: center; + margin-top: 20px; +} + +.nivel { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 0 8px; +} + +.nivel span { + color: var(--c12); + font-family: Lato; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + + +.secHeader { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 8px; +} + +.cardSecTitle { + color: var(--c12); + font-family: 'Raleway',sans-serif; + font-size: 14px; + font-style: normal; + font-weight: 400; + line-height: normal; + +} + +.qtdCards { + font-family: Lato; + font-weight: 700; +} + +.addCard { + padding: 8px 16px; + border-radius: 8px; + color: var(--r1); + background-color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + border: #F2E5FF; + margin-right: 15px; +} + +.wrapperEstudar { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 8px; + width: 100%; + align-items: center; +} + +.qtdEstudar { + color: var(--r12); + font-family: Lato; + font-size: 32px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.btnEstudar { + padding: 8px 16px; + border-radius: 8px; + background-color: var(--r7); + color: var(--r1); + font-family: 'Raleway',sans-serif; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + border: none; +} + +.saveDeck { + background-color: var(--r7); + color: var(--r1); + border: none; + border-radius: 8px; +} + +.selectedDeck { + background-color: #07001B; +} + +.caixasBody { + padding: 16px; + display: flex; + gap: 16px; + flex-wrap: wrap; + width: 100%; + box-shadow: 1px 0px 0px 0px var(--c3); +} + +.caixa { + display: flex; + width: 224px; + height: fit-content; + padding: 16px; + flex-direction: column; + justify-content: center; + align-items: flex-start; + gap: 8px; + + border-radius: 8px; + border: 1px solid var(--c7); + + &.active { + border-top: 1px solid var(--r7); + border-right: 2px solid var(--r7); + border-bottom: 2px solid var(--r7); + border-left: 1px solid var(--r7); + } +} + +.tituloCaixa { + color: var(--r12); + font-family: Raleway; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; + margin: 0; +} + +.descCaixa { + color: var(--c10); + font-family: Lato; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.listaCaixas { + display: flex; + align-items: center; + gap: 8px; + list-style: none; + margin: 0; + padding: 0; +} + +.noCardSelected { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 16px; + padding: 16px; + width: 36%; + min-width: 320px; +} + +.img { + width: 100px; + height: 100px; +} + +.infoText { + color: var(--c7); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.cardInfo { + + flex-direction: column; + + align-items: center; + width: 36%; + min-width: 320px; + + height: 500px; + display: inline-block; + +} + +.wrapper { + height: 100%; + margin: 0 auto; + background-color: #5f5e5e; + width: 1px; +} + +.cardHeader { + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 8px 16px; +} + +.cardTitle { + color: var(--c12); + font-family: Raleway; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: normal; +} + +.wrapperNiveis { + display: flex; + justify-content: center; +} + +.nivel { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 4px; + padding: 0 8px; +} + +.nivel span { + color: var(--c12); + font-family: Lato; + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.secHeader { + display: flex; + justify-content: space-between; + align-items: center; + width: 100%; + padding: 16px 8px; +} + +.cardSecTitle { + color: var(--c12); + font-family: Raleway; + font-size: 20px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.qtdCards { + font-family: Lato; + font-weight: 700; +} + +.addCard { + padding: 8px 16px; + border-radius: 8px; + color: var(--r1); + background-color: var(--r12); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.wrapperEstudar { + display: flex; + flex-direction: column; + gap: 16px; + padding: 16px 8px; + width: 100%; + align-items: center; +} + +.qtdEstudar { + color: var(--r12); + font-family: Lato; + font-size: 32px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.btnEstudar { + padding: 8px 16px; + border-radius: 8px; + background-color: var(--r7); + color: var(--r1); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; +} + +.wrapperTabs { + display: flex; + flex-direction: column; + gap: 10px; + align-items: flex-start; + padding: 8px; + background-color: var(--r1); + border-radius: 8px; + width: 100%; +} + +.nav { + display: flex; + background-color: var(--r12); + border-radius: 8px; + padding: 4px; +} + +.tab { + color: var(--r1); + font-family: Lato; + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: normal; + background-color: transparent; + border-radius: 4px; + padding: 4px 8px; +} + +.tab.active { + background-color: var(--r1); + color: var(--r12); +} + +.tarefas { + display: flex; + flex-direction: column; + gap: 10px; + width: 100%; +} + +.tarefa { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + padding: 16px; + border-radius: 8px; + background-color: transparent; + width: 100%; + border: 1px solid var(--r12); +} + +.tarefaTitle { + color: var(--r12); + font-family: Raleway; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} + +.newDeck { + justify-content: center; + display: flex; + align-items: center; + + width: 224px; + height: 72px; + + + + border-radius: 8px; + border: 1px solid var(--c7); + + + } + diff --git a/app/src/components/app/flashcards/components/DeckForm.jsx b/app/src/components/app/flashcards/components/DeckForm.jsx new file mode 100644 index 0000000..9cb962f --- /dev/null +++ b/app/src/components/app/flashcards/components/DeckForm.jsx @@ -0,0 +1,38 @@ +import React, { useState } from 'react'; +import style from './DeckForm.module.css' +import { UilTimes } from '@iconscout/react-unicons' +import { UilCheck } from '@iconscout/react-unicons' + +const DeckForm = ({ deck, onSave }) => { + const [name, setName] = useState(deck?.name || ''); + + const handleSubmit = (e) => { + if (name.trim() !== '') { + e.preventDefault(); + onSave({ ...deck, name }); + } + else{ + alert("informe um valor valido") + } + }; + + return ( + +
+
+ + + +
+
+

Novo Deck

+ setName(e.target.value)} /> + + ); +}; + +export default DeckForm; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/DeckForm.module.css b/app/src/components/app/flashcards/components/DeckForm.module.css new file mode 100644 index 0000000..e9ba09a --- /dev/null +++ b/app/src/components/app/flashcards/components/DeckForm.module.css @@ -0,0 +1,232 @@ +:root{ + + --r1:#F2E5FF; + --r3:#B469FF; + --r12:#07001B; + --r7:#511ABE; + --c1:#F3EDFA; + +} + +.small-card { + background:var(--c1); + font-family: "Roboto", sans-serif; + font-weight: bold; + font-size: 16px; + width: 300px; + height: 250px; + margin-left: auto; + margin-right: auto; + margin-top: 8px; + border-radius: 8px; + box-shadow: 2px 1px 1px rgb(209, 209, 209); + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.edit-card-center { + width: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.edit-card-separator { + height: 1px; + width: 40%; + background-color: black; + display: flex; +} + +.swap-button { + cursor: pointer; + font-size: 24px; + border: 1px solid black; +} + +.edit-card-side-choice { + + color: var(--r12); + font-family: 'Raleway',sans-serif; +font-size: 14px; +font-weight: 600; +line-height: 16.44px; +text-align: left; + +} + +.small-card-buttons { + width: 100%; + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: flex-start; +} + +.edit-button { + width: 1.5em; + height: 1.5em; + + border-radius: 100%; + padding: 0.5em; + cursor: pointer; + margin: 0.5em; +} + +.delete-button { + width: 1.5em; + height: 1.5em; + background-color: rgb(193, 205, 216); + border-radius: 100%; + padding: 0.5em; + cursor: pointer; + margin: 0.5em; +} + +.quiz-card { + display: flex; + flex-direction: column; + width: 600px; + height: 400px; + background-color: rgb(233, 233, 233); + margin: 1em; + border-radius: 24px; + justify-content: space-between; + align-items: center; +} + +.quiz-card-top { + width: 100%; + display: flex; + justify-content: flex-end; + margin-top: 1em; + margin-right: 2em; + margin-bottom: -4em; +} + +.quiz-card-content { + font-size: 36px; +} + +.flip-card-button { + height: 2em; + width: 2em; + cursor: pointer; +} + +.edit-card-buttons { + display: flex; + margin-bottom: 0.5em; + font-family: "Roboto", sans-serif; + + justify-content: center; +} + +.edit-card-input { + width: 200px; + text-align: center; + margin-bottom: 2em; + background: var(--r1); + border-radius: 8px; + +} + +.result-icon { + margin: auto; + margin-bottom: 20px; + width: 20%; + background-size: cover; +} + +input { + font-family: "Roboto", sans-serif; + margin: auto; + width: 250px; + border-radius: 20px; + padding: 10px; + border: none; + outline: none; + +} + + +.buttonCards { + font-family: "Roboto", sans-serif; + font-weight: bolder; + margin: auto; + width: 230px; + padding: 10px; + border: none; + border-radius: 20px; + cursor: pointer; +} + +.buttonCards:hover { + background-color: lightsteelblue; +} + +.Salvar { + background: var(--r7); + border: none; + border-radius: 8px; + float: right; + +} + +.Fechar { + + background: var(--r12); + border: none; + border-radius: 8px; + + float: left; + + + +} + +.newCard { + font-family: 'Raleway',sans-serif; +font-size: 14px; +font-weight: 500; +line-height: 16.44px; +text-align: center; +color: var(--r12); + +} + +.icon { + margin-left: auto; + margin-right: auto; + display: flex; +} + +.deleteButton { + background-color: #DF1515; + border-radius:8px ; + border: none; + width: 112px; + height: 30px; + color: white; +margin-top: 90px; +float: right; +} + +.deckTitle { + +display: flex; +justify-content: center; +font-family: 'Raleway',sans-serif; +font-weight: 500; +} + +.input { + margin-left: auto; + margin-right: auto; + display: flex; + background-color: var(--r1); + border-radius: 8px; + margin-top: 35px; +} \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/FlashCardForm.jsx b/app/src/components/app/flashcards/components/FlashCardForm.jsx new file mode 100644 index 0000000..a4e46a8 --- /dev/null +++ b/app/src/components/app/flashcards/components/FlashCardForm.jsx @@ -0,0 +1,61 @@ +import React, { useState } from 'react'; +import style from './FlashCardForm.module.css' +import { UilTimes } from '@iconscout/react-unicons' +import { UilCheck } from '@iconscout/react-unicons' + +const FlashcardForm = ({ flashcard, onSave }) => { + const [question, setQuestion] = useState(flashcard?.question || ''); + const [answer, setAnswer] = useState(flashcard?.answer || ''); + + const handleSubmit = (e) => { + if(question.trim() !== '' && answer.trim() !== ''){ + e.preventDefault(); + onSave({ ...flashcard, question, answer }); + } + else{ + alert("Preencha o formulário") + } + + }; + + const handleSave = () => { + if (question && answer) { + onSave({ ...flashcard, question, answer }); + } + }; + + return ( +
+ +
+
+ + + +
+
+

Novo cartão

+
+ +

Frente

+ setQuestion(e.target.value)} /> +

Verso

+ setAnswer(e.target.value)} /> +
+ +
+ ); +}; + +export default FlashcardForm; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/FlashCardForm.module.css b/app/src/components/app/flashcards/components/FlashCardForm.module.css new file mode 100644 index 0000000..d3df3a8 --- /dev/null +++ b/app/src/components/app/flashcards/components/FlashCardForm.module.css @@ -0,0 +1,256 @@ +:root{ + + --r1:#F2E5FF; + --r3:#B469FF; + --r12:#07001B; + --r7:#511ABE; + --c1:#F3EDFA; + + } + + .small-card { + background:var(--c1); + font-family: "Roboto", sans-serif; + font-weight: bold; + font-size: 16px; + width: 300px; + height: 250px; + margin-left: auto; + margin-right: auto; + margin-top: 8px; + border-radius: 8px; + box-shadow: 2px 1px 1px rgb(209, 209, 209); + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + } + + .edit-card-center { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + } + + .edit-card-separator { + height: 1px; + width: 40%; + background-color: black; + display: flex; + } + + .swap-button { + cursor: pointer; + font-size: 24px; + border: 1px solid black; + } + + .editCardSideChoice { + + color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 14px; + font-weight: 600; + line-height: 16.44px; + text-align: left; + margin-left: 0; + + } + + .small-card-buttons { + width: 100%; + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: flex-start; + } + + .edit-button { + width: 1.5em; + height: 1.5em; + + border-radius: 100%; + padding: 0.5em; + cursor: pointer; + margin: 0.5em; + } + + .delete-button { + width: 1.5em; + height: 1.5em; + background-color: rgb(193, 205, 216); + border-radius: 100%; + padding: 0.5em; + cursor: pointer; + margin: 0.5em; + } + + .quiz-card { + display: flex; + flex-direction: column; + width: 600px; + height: 400px; + background-color: rgb(233, 233, 233); + margin: 1em; + border-radius: 24px; + justify-content: space-between; + align-items: center; + } + + .quiz-card-top { + width: 100%; + display: flex; + justify-content: flex-end; + margin-top: 1em; + margin-right: 2em; + margin-bottom: -4em; + } + + .quiz-card-content { + font-size: 36px; + } + + .flip-card-button { + height: 2em; + width: 2em; + cursor: pointer; + } + + .edit-card-buttons { + display: flex; + margin-bottom: 0.5em; + font-family: "Roboto", sans-serif; + + justify-content: center; + } + + .editCardInput { + min-width: 340px; + text-align: center; + margin-bottom: 2em; + background: var(--r1); + border-radius: 8px; + + } + + .result-icon { + margin: auto; + margin-bottom: 20px; + width: 20%; + background-size: cover; + } + + input { + font-family: "Roboto", sans-serif; + margin: auto; + width: 250px; + border-radius: 8px; + padding: 10px; + border: none; + outline: none; + + } + + + .buttonCards { + font-family: "Roboto", sans-serif; + font-weight: bolder; + margin: auto; + width: 230px; + padding: 10px; + border: none; + border-radius: 20px; + cursor: pointer; + } + + .buttonCards:hover { + background-color: lightsteelblue; + } + + .btnSalvar { + background: var(--r7); + border: none; + border-radius: 8px; + width: 35px; + height: 30px; + float: right; + margin-left: 50px; + } + + .btnFechar { + + background: var(--r12); + border: none; + border-radius: 8px; + width: 35px; + height: 30px; + float: left; + margin-right: 50px; + justify-content: center; + align-items: center; + display: flex; + + + } + + .newCard { + font-family: 'Raleway',sans-serif; + font-size: 14px; + font-weight: 500; + line-height: 16.44px; + text-align: center; + color: var(--r12); + + } + + .icon { + margin-left: auto; + margin-right: auto; + display: flex; + } + + .deleteButton { + background-color: #DF1515; + border-radius:8px ; + border: none; + width: 112px; + height: 30px; + color: white; + margin-top: 90px; + float: right; + } + + .deckTitle { + + display: flex; + justify-content: center; + font-family: 'Raleway',sans-serif; + font-weight: 500px; + } + + .input { + margin-left: auto; + margin-right: auto; + display: flex; + } + + .Salvar { + background: var(--r7); + border: none; + border-radius: 8px; + float: right; + + } + + .Fechar { + + background: var(--r12); + border: none; + border-radius: 8px; + + float: left; + + + + } \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/Flashcardss.jsx b/app/src/components/app/flashcards/components/Flashcardss.jsx new file mode 100644 index 0000000..acb8c17 --- /dev/null +++ b/app/src/components/app/flashcards/components/Flashcardss.jsx @@ -0,0 +1,13 @@ +import React from 'react'; + +const Flashcard = ({ flashcard, onEdit }) => { + return ( +
+

{flashcard.question}

+

{flashcard.answer}

+ +
+ ); +}; + +export default Flashcard; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/Modal.jsx b/app/src/components/app/flashcards/components/Modal.jsx new file mode 100644 index 0000000..b1c87d0 --- /dev/null +++ b/app/src/components/app/flashcards/components/Modal.jsx @@ -0,0 +1,14 @@ +import React from 'react'; +import ReactModal from 'react-modal'; + +ReactModal.setAppElement('#root'); + +const Modal = ({ isOpen, onRequestClose, children }) => { + return ( + + {children} + + ); +}; + +export default Modal; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/StudyModal.jsx b/app/src/components/app/flashcards/components/StudyModal.jsx new file mode 100644 index 0000000..d1dd604 --- /dev/null +++ b/app/src/components/app/flashcards/components/StudyModal.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import Modal from 'react-modal'; +import style from './StudyModal.module.css' +import {UilTimes} from '@iconscout/react-unicons' +import {UilCheck} from '@iconscout/react-unicons' +import { UilStepForward } from '@iconscout/react-unicons' +import { UilStepBackwardAlt } from '@iconscout/react-unicons' +import { UilSkipForwardAlt } from '@iconscout/react-unicons' + +const customStyles = { + content: { + top: '50%', + left: '50%', + right: 'auto', + bottom: 'auto', + marginRight: '-50%', + transform: 'translate(-50%, -50%)', + width: '500px', // Largura do modal + height: 'fitcontent', // Altura do modal + padding: '20px', // Padding interno + backgroundColor: '#f0f0f0', // Cor de fundo do modal + borderRadius: '10px', // + boxShadow: '0 4px 8px rgba(0, 0, 0, 0.1)', // + }, + overlay: { + backgroundColor: 'rgba(0, 0, 0, 0.5)', // Fundo semitransparente + }, +}; + +const StudyFlashcardsModal = ({ + flashcards, + onClose, + currentCardIndex, + showAnswer, + handleNextCard, + handleShowAnswer, + closeStudyModal +}) => { + const currentCard = flashcards[currentCardIndex]; + + + + return ( + + +
+
+ + + + + + +
+ +
+

{currentCard.question}

+ {showAnswer &&

{currentCard.answer}

} +
+
+ +
+ + < UilStepBackwardAlt className={style.skip} onClick={handleNextCard} size='30px' color='var(--r7)'> + + + + +
+ +
+ ); +}; + +export default StudyFlashcardsModal; \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/StudyModal.module.css b/app/src/components/app/flashcards/components/StudyModal.module.css new file mode 100644 index 0000000..7d33358 --- /dev/null +++ b/app/src/components/app/flashcards/components/StudyModal.module.css @@ -0,0 +1,145 @@ +:root{ + + --r1:#F2E5FF; + --r3:#B469FF; + --r12:#07001B; + --r7:#511ABE; + --c1:#F3EDFA; + + } + + .smallcard { + background:var(--c1); + font-family: "Roboto", sans-serif; + font-weight: bold; + font-size: 16px; + width: 300px; + height: 250px; + margin-left: auto; + margin-right: auto; + margin-top: 8px; + border-radius: 8px; + box-shadow: 2px 1px 1px rgb(209, 209, 209); + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + } + + + .editCardSideChoice { + + color: var(--r12); + font-family: 'Raleway',sans-serif; + font-size: 14px; + font-weight: 600; + line-height: 16.44px; + text-align: left; + margin-left: 0; + + } + + + .pergunta { + text-align: center; + font-size: 22px; + font-weight: 400; + } + + .resposta { + text-align: center; + justify-content: center; + align-items: center; + font-size: 30px; + font-weight: 600; + margin-top: 70px; + } + .editCardInput { + min-width: 340px; + text-align: center; + margin-bottom: 2em; + background: var(--r1); + border-radius: 8px; + + } + + .result-icon { + margin: auto; + margin-bottom: 20px; + width: 20%; + background-size: cover; + } + + input { + font-family: "Roboto", sans-serif; + margin: auto; + width: 250px; + border-radius: 8px; + padding: 10px; + border: none; + outline: none; + + } + + + + .icon { + margin-left: auto; + margin-right: auto; + display: flex; + + } + + .skip { + float: right; + align-items: center; + + + } + + .deckTitle { + + display: flex; + justify-content: center; + font-family: 'Raleway',sans-serif; + font-weight: 500px; + } + + .input { + margin-left: auto; + margin-right: auto; + display: flex; + } + + .Salvar { + background: var(--r7); + border: none; + border-radius: 8px; + float: right; + + } + + .Fechar { + + background: var(--vermelho); + border: none; + border-radius: 8px; + + float: left; + + + + } + + .controls { + margin-top: 100px; + + display: flex; + + } + +.mostrarRespostaBtn { + margin-left: auto; + margin-right: auto; + +} \ No newline at end of file diff --git a/app/src/components/app/flashcards/components/contexts/DeckContext.jsx b/app/src/components/app/flashcards/components/contexts/DeckContext.jsx new file mode 100644 index 0000000..44f5e17 --- /dev/null +++ b/app/src/components/app/flashcards/components/contexts/DeckContext.jsx @@ -0,0 +1,87 @@ +import React, { createContext, useState } from 'react'; + +const DeckContext = createContext(); + +const DeckProvider = ({ children }) => { + const [decks, setDecks] = useState([ + { id: 1, name: 'Deck 1', flashcards: [] }, + + ]); + const [selectedDeck, setSelectedDeck] = useState(null); + const [flashcardToEdit, setFlashcardToEdit] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [isCreatingDeck, setIsCreatingDeck] = useState(false); + + const openModal = (deck, flashcard = null) => { + setSelectedDeck(deck); + setFlashcardToEdit(flashcard); + setIsModalOpen(true); + }; + + const closeModal = () => { + setIsModalOpen(false); + setSelectedDeck(null); + setFlashcardToEdit(null); + setIsCreatingDeck(false); + }; + + const saveFlashcard = (flashcard) => { + const updatedDecks = decks.map(deck => { + if (deck.id === selectedDeck.id) { + const flashcards = flashcard.id + ? deck.flashcards.map(fc => (fc.id === flashcard.id ? flashcard : fc)) + : [...deck.flashcards, { ...flashcard, id: Date.now() }]; + return { ...deck, flashcards }; + } + return deck; + }); + setDecks(updatedDecks); + closeModal(); + }; + + const saveDeck = (deck) => { + const updatedDecks = deck.id + ? decks.map(d => (d.id === deck.id ? deck : d)) + : [...decks, { ...deck, id: Date.now(), flashcards: [] }]; + setDecks(updatedDecks); + closeModal(); + }; + + const addDeck = (deckName) => { + const newDeck = { + id: Math.random().toString(36).substr(2, 9), // Gera um ID único para o deck + name: deckName, + flashcards: [] // Inicializa com uma lista vazia de flashcards + }; + + setDecks([...decks, newDeck]); + }; + + const selectDeck = (deckId) => { + const selected = decks.find(deck => deck.id === deckId); + setSelectedDeck(selected); + }; + + return ( + + {children} + + ); +}; + +export { DeckProvider, DeckContext }; \ No newline at end of file diff --git a/app/src/components/app/listas/BtnListas.jsx b/app/src/components/app/listas/BtnListas.jsx new file mode 100644 index 0000000..c1d6585 --- /dev/null +++ b/app/src/components/app/listas/BtnListas.jsx @@ -0,0 +1,59 @@ +import PropTypes from "prop-types"; +import { useState } from "react"; +import { UilSquareShape, UilPen, UilTrash } from "@iconscout/react-unicons"; +import ModalExcluir from "../modal/ModalExcluir"; +import { NavLink } from "react-router-dom"; + +const BtnListas = ({ lista, style }) => { + const [modalExcluirAberto, setModalExcluirAberto] = useState(false); + + const handleOpenModalExcluir = () => { + setModalExcluirAberto(true); + }; + + const handleCloseModalExcluir = () => { + setModalExcluirAberto(false); + }; + + return ( + <> + + isActive ? `${style.active} ${style.item}` : `${style.item}` + } + > + {lista.emoji ? ( + + ) : ( + + )} + {lista.nome} + + + + + + + {modalExcluirAberto && ( + + )} + + ); +}; + +BtnListas.propTypes = { + lista: PropTypes.object.isRequired, + style: PropTypes.object.isRequired, +}; + +export default BtnListas; diff --git a/app/src/components/app/markdown/Markdown.jsx b/app/src/components/app/markdown/Markdown.jsx index b187f80..70f39b8 100644 --- a/app/src/components/app/markdown/Markdown.jsx +++ b/app/src/components/app/markdown/Markdown.jsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useRef } from "react"; import style from "./Markdown.module.css"; import { UilBold, @@ -9,6 +9,7 @@ import { UilCheckSquare, UilCommentAlt, UilArrow, + UilFileDownload, } from "@iconscout/react-unicons"; import MdEditor from "./MdEditor"; import MdPreview from "./MdPreview"; @@ -17,27 +18,80 @@ const Markdown = () => { const storedMarkdown = localStorage.getItem("MARKDOWN"); const [markdown, setMarkdown] = useState(storedMarkdown ? storedMarkdown : "# Olá, mundo!"); + const textAreaRef = useRef(null); const callback = (markdown) => { setMarkdown(markdown); localStorage.setItem("MARKDOWN", markdown); } + const insertTag = (start, end) => { + const textArea = textAreaRef.current; + const startPos = textArea.selectionStart; + const endPos = textArea.selectionEnd; + const text = textArea.value; + + const newText = text.substring(0, startPos) + start + text.substring(startPos, endPos) + end + text.substring(endPos); + + setMarkdown(newText); + + textArea.selectionStart = startPos + start.length; + textArea.selectionEnd = endPos + start.length; + } + + const bold = () => { + insertTag("**", "**"); + } + const italic = () => { + insertTag("_", "_"); + } + const size = () => { + insertTag("# ", ""); + } + const listUl = () => { + insertTag("- ", ""); + } + const listOl = () => { + insertTag("1. ", ""); + } + const checkSquare = () => { + insertTag("- [ ] ", ""); + } + const quoteBlock = () => { + insertTag("\n> ", ""); + } + const codeBlock = () => { + insertTag('\n```\n', '\n```\n'); + } + + const gerarArquivo = () => { + const blob = new Blob([markdown], { type: "text/markdown" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = "arquivo.md"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } + return (
- - - - - - - - + + + + + + + + +
- +
); diff --git a/app/src/components/app/markdown/MdEditor.jsx b/app/src/components/app/markdown/MdEditor.jsx index 2d67544..22b11dd 100644 --- a/app/src/components/app/markdown/MdEditor.jsx +++ b/app/src/components/app/markdown/MdEditor.jsx @@ -1,10 +1,10 @@ -import React from 'react' +import { useRef } from 'react' import PropTypes from 'prop-types' -const MdEditor = ({style, markdown, setMarkdown}) => { +const MdEditor = ({style, markdown, setMarkdown, textAreaRef}) => { return (
-