diff --git a/.gitignore b/.gitignore index 45b9b19..c7f34ac 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,11 @@ pnpm-debug.log* lerna-debug.log* node_modules +package.json +package-lock.json app/node_modules +backend/node_modules +backend/.env dist dist-ssr *.local 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 0ec29d2..9f117e3 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -2083,6 +2083,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2288,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", @@ -2461,6 +2465,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -3111,6 +3123,25 @@ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -3120,6 +3151,19 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4370,6 +4414,11 @@ "react": ">=0.14.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4528,6 +4577,17 @@ "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", 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 eda3fde..b94d6b3 100644 --- a/app/src/components/App.jsx +++ b/app/src/components/App.jsx @@ -44,10 +44,16 @@ function App() { }, [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 a2f3da4..c0dcb13 100644 --- a/app/src/components/LoginPage.jsx +++ b/app/src/components/LoginPage.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import { useState } from "react"; import style from "./LoginPage.module.css"; import astronauta from "../assets/astronauta.svg"; import PropTypes from "prop-types"; @@ -168,7 +168,8 @@ const LoginPage = ({ acao }) => { Entrar

- Ainda não possui uma conta? Cadastre-se + Ainda não possui uma conta?{" "} + Cadastre-se

diff --git a/app/src/components/app/PageCard.module.css b/app/src/components/app/PageCard.module.css new file mode 100644 index 0000000..2e8e843 --- /dev/null +++ b/app/src/components/app/PageCard.module.css @@ -0,0 +1,674 @@ +.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; + gap: 8px; + align-items: center; + width: 36%; + min-width: 320px; + padding: 16px; +} + +.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; + 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; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; +} \ No newline at end of file diff --git a/app/src/components/app/PageCards.jsx b/app/src/components/app/PageCards.jsx new file mode 100644 index 0000000..2cf1fea --- /dev/null +++ b/app/src/components/app/PageCards.jsx @@ -0,0 +1,25 @@ +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 ( + <> + + + + + + + + + ); +} + +export default PageCard; diff --git a/app/src/components/app/PageTasks.jsx b/app/src/components/app/PageTasks.jsx new file mode 100644 index 0000000..2e55908 --- /dev/null +++ b/app/src/components/app/PageTasks.jsx @@ -0,0 +1,180 @@ +import { useState, useEffect } from "react"; +import SecSidebar from "./SecSidebar"; +import style from "./PageTasks.module.css"; +import { + UilArrowToRight, + UilLeftArrowToLeft, + UilSlidersV, + UilGrid, + UilAngleDown, + UilCircle, + UilInbox, +} from "@iconscout/react-unicons"; +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 = ({ tipo }) => { + const [sidebar, setSidebar] = useState(true); + 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)} + /> + )} +

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

+
+
+ + +
+
+
+ {tasks && + tasks.map((task) => ( + + ))} +
+
+ {tarefaSelecionada && ( + + )} + + ); +}; + +export default PageTasks; diff --git a/app/src/components/app/tasks/PageTasks.module.css b/app/src/components/app/PageTasks.module.css similarity index 79% rename from app/src/components/app/tasks/PageTasks.module.css rename to app/src/components/app/PageTasks.module.css index 54a8530..ee71d8e 100644 --- a/app/src/components/app/tasks/PageTasks.module.css +++ b/app/src/components/app/PageTasks.module.css @@ -1,27 +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; } } @@ -29,22 +30,23 @@ .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; } } @@ -52,7 +54,7 @@ .taskWrapper { display: flex; flex-direction: column; - gap: 8px; + gap: 0.5rem; width: 100%; } @@ -60,10 +62,10 @@ 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); @@ -73,13 +75,13 @@ .taskCheck { display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; } .taskNome { color: var(--c12); font-family: Raleway; - font-size: 12px; + font-size: 1rem; font-style: normal; font-weight: 500; line-height: normal; @@ -88,14 +90,14 @@ .taskOptions { display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; flex-wrap: wrap; } .taskOptionsLista { color: var(--c10); font-family: Raleway; - font-size: 12px; + font-size: 0.8rem; font-style: normal; font-weight: 400; line-height: normal; @@ -105,7 +107,7 @@ 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 a0f684b..e873e84 100644 --- a/app/src/components/app/SecSidebar.jsx +++ b/app/src/components/app/SecSidebar.jsx @@ -1,19 +1,13 @@ -import { useState, useContext } from "react"; +import { useState} from "react"; import PropTypes from "prop-types"; import style from "./SecSidebar.module.css"; import { UilPlus, UilSun, UilInbox, - UilCalendarAlt, - UilPuzzlePiece, - UilAngleUp, - UilAngleDown, - UilTagAlt, UilCheckCircle, UilExclamationCircle, UilTrashAlt, - UilSquareShape, } from "@iconscout/react-unicons"; import ModalNewList from "./modal/ModalNewList"; import ModalNewTask from "./modal/ModalNewTask"; @@ -22,14 +16,10 @@ import data from "@emoji-mart/data"; import { init } from "emoji-mart"; init({ data }); import { useListaContext } from "../../hooks/useListaContext"; -import { useUserContext } from "../../hooks/useUserContext"; -import { useTaskContext } from "../../hooks/useTaskContext"; import { NavLink } from "react-router-dom"; -const SecSidebar = ({ isOpen, tipo }) => { +const SecSidebar = ({ isOpen }) => { const { listas } = useListaContext(); - const { user } = useUserContext(); - const { dispatch } = useTaskContext(); const [showModalNewList, setShowModalNewList] = useState(false); const [showModalNewTask, setShowModalNewTask] = useState(false); @@ -47,8 +37,8 @@ const SecSidebar = ({ isOpen, tipo }) => { return (
- { isActive ? `${style.active} ${style.item}` : `${style.item}` } > - + Hoje { isActive ? `${style.active} ${style.item}` : `${style.item}` } > - + Entrada { isActive ? `${style.active} ${style.item}` : `${style.item}` } > - + Concluídas { isActive ? `${style.active} ${style.item}` : `${style.item}` } > - + Atrasadas { isActive ? `${style.active} ${style.item}` : `${style.item}` } > - + Excluídas
@@ -104,7 +94,7 @@ const SecSidebar = ({ isOpen, tipo }) => {
Listas
@@ -116,8 +106,8 @@ const SecSidebar = ({ isOpen, tipo }) => {
) : ( - )} diff --git a/app/src/components/app/SecSidebar.module.css b/app/src/components/app/SecSidebar.module.css index 4e75bd9..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,79 +21,111 @@ .secSidebar { display: flex; flex-direction: column; - min-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); +} - &:hover { - .badge { - display: flex; - } - } +.item.add:hover { + background-color: var(--r7); + color: var(--c1); - &.active { - background-color: var(--r1); + svg { + fill: var(--c1); } +} - .badge { - color: var(--c7); - font-family: Raleway; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - position: absolute; - display: none; - align-items: center; - gap: 4px; - right: 8px; - top: auto; - bottom: auto; - } +.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 { @@ -109,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 3339cf4..5b2ef2e 100644 --- a/app/src/components/app/Sidebar.jsx +++ b/app/src/components/app/Sidebar.jsx @@ -30,7 +30,7 @@ const Sidebar = () => { {({ isActive }) => ( )} @@ -39,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/cards/PageCards.jsx b/app/src/components/app/cards/PageCards.jsx deleted file mode 100644 index 90feb2b..0000000 --- a/app/src/components/app/cards/PageCards.jsx +++ /dev/null @@ -1,118 +0,0 @@ -import { useState } from "react"; -import style from "./PageCard.module.css"; -import { UilBox, UilEdit } from "@iconscout/react-unicons"; -import logo from "../../../assets/logo-cinza.png"; - -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 -

-
- )} - - ); -} - -export default PageCard; 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/cards/PageCard.module.css b/app/src/components/app/flashcards/components/Deck.module.css similarity index 59% rename from app/src/components/app/cards/PageCard.module.css rename to app/src/components/app/flashcards/components/Deck.module.css index 9fedc32..c55de84 100644 --- a/app/src/components/app/cards/PageCard.module.css +++ b/app/src/components/app/flashcards/components/Deck.module.css @@ -1,3 +1,170 @@ + + +@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; @@ -82,13 +249,23 @@ } .cardInfo { - display: flex; + flex-direction: column; - gap: 8px; + align-items: center; width: 36%; min-width: 320px; - padding: 16px; + + height: 500px; + display: inline-block; + +} + +.wrapper { + height: 100%; + margin: 0 auto; + background-color: #5f5e5e; + width: 1px; } .cardHeader { @@ -142,7 +319,7 @@ .cardSecTitle { color: var(--c12); font-family: Raleway; - font-size: 14px; + font-size: 20px; font-style: normal; font-weight: 400; line-height: normal; @@ -256,4 +433,21 @@ font-style: normal; font-weight: 400; line-height: normal; -} \ No newline at end of file +} + +.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 index 8b2c7f8..c1d6585 100644 --- a/app/src/components/app/listas/BtnListas.jsx +++ b/app/src/components/app/listas/BtnListas.jsx @@ -25,16 +25,16 @@ const BtnListas = ({ lista, style }) => { } > {lista.emoji ? ( - + ) : ( - + )} {lista.nome} - + diff --git a/app/src/components/app/modal/ModalConfig.jsx b/app/src/components/app/modal/ModalConfig.jsx new file mode 100644 index 0000000..51912ab --- /dev/null +++ b/app/src/components/app/modal/ModalConfig.jsx @@ -0,0 +1,62 @@ +import React, { useState } from "react"; + +function Modal({ onClose, loggedIn, onLogout, onChangeAvatar }) { + const [avatar, setAvatar] = useState(""); // Estado para o avatar, caso queira permitir que o usuário mude + + return ( +
+
+ × +

Configurações

+ {loggedIn ? ( + <> + + + + ) : ( +

Faça login para acessar as configurações.

+ )} +
+
+ ); +} + +function App() { + const [modalOpen, setModalOpen] = useState(false); + const [loggedIn, setLoggedIn] = useState(false); + + // Função para deslogar o usuário + function handleLogout() { + // lógica para deslogar o usuário aqui + alert("Usuário deslogado com sucesso!"); + setLoggedIn(false); + } + + // Função para mudar o avatar do usuário + function handleChangeAvatar() { + // lógica para mudar o avatar aqui + // Por exemplo, você pode abrir um seletor de arquivo para o usuário escolher uma imagem + alert("Implemente a lógica para mudar o avatar aqui!"); + } + + // Função para abrir/fechar o modal + function toggleModal() { + setModalOpen(!modalOpen); + } + + return ( +
+ + {modalOpen && ( + + )} +
+ ); +} + +export default App; diff --git a/app/src/components/app/PageNotes.jsx b/app/src/components/app/modal/ModalConfig.module.css similarity index 100% rename from app/src/components/app/PageNotes.jsx rename to app/src/components/app/modal/ModalConfig.module.css diff --git a/app/src/components/app/modal/ModalNewList.jsx b/app/src/components/app/modal/ModalNewList.jsx index d8496d0..94007bd 100644 --- a/app/src/components/app/modal/ModalNewList.jsx +++ b/app/src/components/app/modal/ModalNewList.jsx @@ -117,10 +117,10 @@ const ModalNewList = ({setShowModalNewList}) => {
{tabEmoji === true ? ( <> - + ) : ( - + )} { { ))} diff --git a/app/src/components/app/modal/ModalNewList.module.css b/app/src/components/app/modal/ModalNewList.module.css index 1c8ec9a..4501a20 100644 --- a/app/src/components/app/modal/ModalNewList.module.css +++ b/app/src/components/app/modal/ModalNewList.module.css @@ -1,7 +1,7 @@ .modal { - border-radius: 8px; + border-radius: 0.5rem; background: var(--c1); - border: 1px solid var(--c9); + border: 1px solid var(--c12); height: fit-content; width: fit-content; position: absolute; @@ -13,18 +13,18 @@ .wrapper { display: flex; flex-direction: column; - gap: 8px; - padding: 8px; + gap: 0.5rem; + padding: 0.5rem; } .newList { display: flex; - gap: 4px; - padding: 8px; + gap: 0.25rem; + padding: 0.5rem; align-items: center; - border-radius: 8px; - background: var(--c2); - border: 1px solid var(--c9); + border-radius: 0.5rem; + background: var(--c1); + border: 1px solid var(--c12); } .inputName { @@ -32,11 +32,10 @@ height: 100%; background: none; border: none; - color: var(--c9); + color: var(--c12); font-family: Raleway; - font-size: 12px; + font-size: 1rem; font-style: normal; - font-weight: 500; line-height: normal; } @@ -46,46 +45,45 @@ .btns { display: flex; - gap: 8px; + gap: 0.5rem; align-items: center; } .btn { - display: flex; - padding: 4px 8px; - align-items: center; - justify-content: center; - gap: 4px; + padding: 0.5rem 0.5rem; + text-align: center; + gap: 0.25rem; width: 100%; color: var(--c12); font-family: Raleway; - font-size: 12px; + font-size: 0.75rem; font-style: normal; font-weight: 500; line-height: normal; - background-color: var(--c2); + background-color: var(--c1); + border: 1px solid var(--c12); + border-radius: 0.5rem; } .btn:hover { - background-color: var(--c3); + background-color: var(--r7); + color: var(--c1); + border: none; } .btn.active { background-color: var(--r7); - color: var(--r1); + color: var(--c1); + border: none; } .items { width: 100%; - padding: 4px 0px; + padding: 0.25rem 0px; display: flex; justify-content: space-evenly; svg { cursor: pointer; } -} - -.container { - width: 280px; } \ No newline at end of file diff --git a/app/src/components/app/modal/ModalNewTask.jsx b/app/src/components/app/modal/ModalNewTask.jsx index b3b1754..022f622 100644 --- a/app/src/components/app/modal/ModalNewTask.jsx +++ b/app/src/components/app/modal/ModalNewTask.jsx @@ -26,12 +26,12 @@ const ModalNewTask = ({ setShowModalNewTask }) => { value: lista._id, label: lista.emoji ? ( <> - + {` ${lista.nome}`} ) : ( <> - + {` ${lista.nome}`} ), @@ -85,10 +85,10 @@ const ModalNewTask = ({ setShowModalNewTask }) => {
- + { @@ -113,35 +113,36 @@ const ModalNewTask = ({ setShowModalNewTask }) => { ...styles, backgroundColor: "var(--c1)", color: "var(--c12)", - border: "var(--c7) 1px solid", + border: "var(--c12) 1px solid", boxShadow: "none", - borderRadius: "8px", + borderRadius: "0.5rem", height: "100%", - fontSize: "12px", + fontSize: "1rem", cursor: "pointer", }), menu: (styles) => ({ ...styles, backgroundColor: "var(--c1)", color: "var(--c12)", - border: "var(--c7) 1px solid", + border: "var(--c12) 1px solid", boxShadow: "none", - borderRadius: "8px", - fontSize: "12px", + borderRadius: "0.5rem", + fontSize: "1rem", }), option: (styles, { isFocused }) => ({ ...styles, backgroundColor: isFocused ? "var(--c1)" : "var(--c1)", color: "var(--c12)", - fontSize: "12px", + fontSize: "1rem", cursor: "pointer", }), singleValue: (styles) => ({ ...styles, color: "var(--c12)", - fontSize: "12px", + fontSize: "1rem", alignItems: "center", display: "flex", + gap: "0.5rem", }), dropdownIndicator: (styles) => ({ ...styles, @@ -155,18 +156,21 @@ const ModalNewTask = ({ setShowModalNewTask }) => { name="data" onClick={handleClickData} > - +
{hasData && (
{ setVencimento(date); + console.log(date); }} + />
)} diff --git a/app/src/components/app/modal/ModalNewTask.module.css b/app/src/components/app/modal/ModalNewTask.module.css index a72d80d..4c96d22 100644 --- a/app/src/components/app/modal/ModalNewTask.module.css +++ b/app/src/components/app/modal/ModalNewTask.module.css @@ -1,7 +1,7 @@ .modal { - border-radius: 8px; + border-radius: 0.5rem; background: var(--c1); - border: 1px solid var(--c9); + border: 1px solid var(--c12); height: fit-content; width: fit-content; position: absolute; @@ -13,18 +13,22 @@ .wrapper { display: flex; flex-direction: column; - gap: 8px; - padding: 8px; + gap: 0.5rem; + padding: 0.5rem; } .newTask { display: flex; - gap: 4px; - padding: 8px; + gap: 0.25rem; + padding: 0.5rem; align-items: center; - border-radius: 8px; + border-radius: 0.5rem; background: var(--c1); - border: 1px solid var(--c9); + border: 1px solid var(--c12); +} + +.newTask > svg { + fill: var(--c12); } .inputName { @@ -32,11 +36,10 @@ height: 100%; background: none; border: none; - color: var(--c9); + color: var(--c12); font-family: Raleway; - font-size: 12px; + font-size: 1rem; font-style: normal; - font-weight: 500; line-height: normal; } @@ -49,7 +52,7 @@ height: 100%; display: flex; align-items: center; - gap: 8px; + gap: 0.5rem; } .selectLista { @@ -59,21 +62,24 @@ .btns { display: flex; - gap: 8px; + gap: 0.5rem; align-items: center; } .btnCalender, .btnTime { display: flex; place-items: center; - padding: 8px; + padding: 0.5rem; background-color: var(--c1); - border: 1px solid var(--c9); - border-radius: 8px; + border: 1px solid var(--c12); + border-radius: 0.5rem; } .btnCalender:hover, .btnTime:hover { - background-color: var(--c2); + background-color: var(--r7); + svg { + fill: var(--c1); + } } .btnCalender.active, .btnTime.active { @@ -83,6 +89,10 @@ } } -.datePicker { - position: relative; +.container { + width: 100%; +} + +.date { + width: 100%; } \ No newline at end of file diff --git a/app/src/components/app/tasks/PageTasks.jsx b/app/src/components/app/tasks/PageTasks.jsx deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/components/pages/Footer.jsx b/app/src/components/pages/Footer.jsx index 85244c8..2462359 100644 --- a/app/src/components/pages/Footer.jsx +++ b/app/src/components/pages/Footer.jsx @@ -5,7 +5,7 @@ const Footer = () => { return (
-

© 2021 - Todos os direitos reservados

+

© 2024 - Todos os direitos reservados

*Este site faz parte do trabalho de conclusão do curso técnico de Desenvolvimento de Sistemas da Etec Dra. Ruth Cardoso. diff --git a/app/src/components/pages/Footer.module.css b/app/src/components/pages/Footer.module.css index 23c11f9..580f666 100644 --- a/app/src/components/pages/Footer.module.css +++ b/app/src/components/pages/Footer.module.css @@ -1,35 +1,42 @@ .footer { - padding: 90px 5%; + padding: 5.5em 5%; display: flex; justify-content: space-between; flex-wrap: wrap; - gap: 64px; + gap: 4em; background-color: var(--r11); color: var(--r1); font-variant-numeric: lining-nums proportional-nums; font-family: Lato; - font-size: 12px; + font-size: 1em; font-style: normal; font-weight: 300; - letter-spacing: 0.36px; + letter-spacing: 0.42px; text-transform: uppercase; } .copy { - max-width: 33%; display: flex; flex-direction: column; - gap: 16px; + text-align: center; + width: 100%; + gap: 1em; + p { + text-wrap: wrap; + } } .social { display: flex; flex-direction: column; - gap: 16px; + flex-wrap: wrap; + align-items: center; + width: 100%; + gap: 1em; ul { list-style: none; display: flex; - gap: 16px; + gap: 1em; justify-content: end; a { @@ -44,24 +51,13 @@ color: var(--r1); font-variant-numeric: lining-nums proportional-nums; font-family: Raleway; - font-size: calc(12px + 2vw); + font-size: calc(1em + 2vw); font-style: normal; font-weight: 700; line-height: normal; letter-spacing: 2.88px; text-align: center; text-transform: uppercase; - - &:hover { - background: linear-gradient( - 90deg, - rgba(242, 229, 255, 1) 0%, - rgba(180, 105, 255, 1) 50%, - rgba(242, 229, 255, 1) 100% - ); - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - } } @media only screen and (max-device-width: 627px) { diff --git a/app/src/components/pages/Header.module.css b/app/src/components/pages/Header.module.css index bf68a92..cb10b33 100644 --- a/app/src/components/pages/Header.module.css +++ b/app/src/components/pages/Header.module.css @@ -3,12 +3,12 @@ justify-content: space-between; flex-wrap: wrap; align-items: center; - padding: 1rem 5%; + padding: 1em 5%; } .brand { img { - max-height: 45px; + height: 2.5em; } } @@ -20,10 +20,10 @@ a { display: flex; align-items: center; - gap: 0.25rem; - padding: 0.5rem 1rem; + gap: 0.5em; + padding: 0.5em 1em; color: var(--r12); - font-size: 16px; + font-size: 1em; &:hover { color: var(--r7); @@ -33,13 +33,13 @@ .buttons { display: flex; - gap: 0.5rem; + gap: 0.5em; a { - padding: 0.5rem 1rem; - font-size: 14px; + padding: 0.5em 1em; + font-size: 1em; font-weight: 500; - border-radius: 0.5rem; + border-radius: 0.5em; letter-spacing: 0.42px; &.login { @@ -68,11 +68,12 @@ } @media screen and (max-width: 937px) { + .navLinks { order: 2; width: 100%; justify-content: center; - margin-top: 1rem; + margin-top: 1em; border-top: 1px solid var(--r7); border-bottom: 1px solid var(--r7); } diff --git a/app/src/components/pages/LandingPage.jsx b/app/src/components/pages/LandingPage.jsx index 9bd40ec..b5cc81d 100644 --- a/app/src/components/pages/LandingPage.jsx +++ b/app/src/components/pages/LandingPage.jsx @@ -1,13 +1,25 @@ -import React from "react"; import Header from "./Header"; import Footer from "./Footer"; import style from "./LandingPage.module.css"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Link } from "react-router-dom"; +import { useEffect, useState } from "react"; const LandingPage = () => { + const [tabAtiva, setTabAtiva] = useState("foco"); + + const handleTabChange = (e) => { + setTabAtiva(e.target.name); + }; + + const [planoAtivo, setPlanoAtivo] = useState("mensal"); + + const handlePlanoChange = (e) => { + setPlanoAtivo(e.target.id); + }; + return ( - <> +

@@ -19,153 +31,208 @@ const LandingPage = () => {

Iniciar jornada - - hero + + hero

Conheça os beneficios

- - - + + +
-
-

- Pomodoro Timer -

-
-

- O CosmicTasks oferece um Pomodoro Timer integrado que facilita o - uso da técnica. -

-

- Basta definir o tempo de pomodoro e o tempo de pausa, e focar no - que é preciso. -

+ {tabAtiva === "foco" && ( +
+

+ Pomodoro Timer +

+
+

+ O CosmicTasks oferece um Pomodoro Timer integrado que facilita + o uso da técnica. +

+

+ Basta definir o tempo de pomodoro e o tempo de pausa, e focar + no que é preciso. +

+
-
+ )} + + {tabAtiva === "organização" && ( +
+

+ Lista de Tarefas +

+
+

+ Crie listas de tarefas para organizar suas atividades diárias. +

+

+ Adicione tarefas, defina prioridades e marque como concluídas. +

+
+
+ )} + {tabAtiva === "produtividade" && ( +
+

+ Flashcards Integrados +

+
+

+ Crie flashcards para estudar e memorizar conteúdos de forma + eficiente. +

+

+ O CosmicTasks oferece um sistema de flashcards para otimizar a + memorização. +

+
+
+ )}
-
-
- -
-

Acompanhe rotinas

-

Use as rotinas para tarefas repetitivas ou hábitos.

-
-
-
- -
-

Totalmente responsivo

-

- Design adaptado para telas de diferentes tamanhos, do celular ao - computador. -

-
-
-
- +
+
+ +
+

Acompanhe rotinas

+

Use as rotinas para tarefas repetitivas ou hábitos.

-

Diferentes temas

-

Modifique a aparência do app escolhendo um dos temas disponíveis.

-
-
-
- +
+
+ +
+

Totalmente responsivo

+

+ Design adaptado para telas de diferentes tamanhos, do celular ao + computador. +

-

Tudo em um só lugar

-

- CosmicTasks reúne tarefas, rotinas, timer, calendário, anotações e - flashcards em um único website. Assim, você não precisa baixar - diversos aplicativos diferentes. -

-
-
-
- +
+
+ +
+

Diferentes temas

+

+ Modifique a aparência do app escolhendo um dos temas disponíveis. +

-

Design simples e intuitivo

-

- O design foi pensado para que o usuário não tenha dificuldade em - entender o que fazer e onde clicar. São utilizadas cores contrastantes - e ícones reconhecíveis. -

-
-
-
-
-
-

Planos

-

Escolha o plano ideal para você

+
+
+ +
+

Tudo em um só lugar

+

+ CosmicTasks reúne tarefas, rotinas, timer, calendário, anotações e + flashcards em um único website. Assim, você não precisa baixar + diversos aplicativos diferentes. +

-
- - - - +
+
+ +
+

Design simples e intuitivo

+

+ O design foi pensado para que o usuário não tenha dificuldade em + entender o que fazer e onde clicar. São utilizadas cores + contrastantes e ícones reconhecíveis. +

-
-
-
- Gratuito -

R$0

- por mês +
+
+
+

Planos

+

Escolha o plano ideal para você

+
+
+ + + +
-
    -
  • Tarefas
  • -
  • Timer
  • -
  • Anotações
  • -
  • Flashcards
  • -
-
-
-
- Premium -

R$20

- por mês +
+
+
+ Gratuito +

R$0

+ por mês +
+
    +
  • Tarefas
  • +
  • Timer
  • +
  • Anotações
  • +
  • Flashcards
  • +
+ +
+
+
+ Premium +

R$20

+ por mês +
+
    +
  • Dashboard aprofundada
  • +
  • Equipes
  • +
+
-
    -
  • Dashboard aprofundada
  • -
  • Equipes
  • -
-
+
+
+

Foco, organização e produtividade.

+ + Iniciar jornada +
-
-
-

Foco, organização e produtividade.

- - Iniciar jornada - +
-