From 7aba236adafe24639e06f8c606fbc754707e6755 Mon Sep 17 00:00:00 2001 From: ArthurVenturi Date: Thu, 17 Apr 2025 17:11:33 -0300 Subject: [PATCH 01/10] =?UTF-8?q?implementa=C3=A7=C3=A3o=20da=20l=C3=B3gic?= =?UTF-8?q?a=20de=20cadastro=20e=20login?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 84 ++++++++++++- public/assets/css/cadastro.css | 4 +- public/assets/css/login.css | 4 +- public/assets/css/performance.css | 157 +++++++++++++++++++++++ public/assets/css/statistics.css | 170 +++++++++++++++++++++++++ src/api/User.jsx | 34 +++++ src/components/App.jsx | 13 +- src/components/Header.jsx | 31 +++-- src/contexts/UserContext.jsx | 41 ++++++ src/pages/Cadastro.jsx | 200 ++++++++++++++++++++---------- src/pages/Login.jsx | 46 ++++++- src/routeTree.gen.ts | 34 ++++- src/routes/performance.lazy.jsx | 107 ++++++++++++++++ src/routes/statistics.lazy.jsx | 164 +++++++++++++++--------- vite.config.js | 22 ++-- 15 files changed, 944 insertions(+), 167 deletions(-) create mode 100644 public/assets/css/performance.css create mode 100644 public/assets/css/statistics.css create mode 100644 src/api/User.jsx create mode 100644 src/contexts/UserContext.jsx create mode 100644 src/routes/performance.lazy.jsx diff --git a/package-lock.json b/package-lock.json index 651c5da..87e11f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5333,6 +5333,51 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", + "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.3", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -5569,15 +5614,18 @@ } }, "node_modules/vite": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", - "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz", + "integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.3", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.12" }, "bin": { "vite": "bin/vite.js" @@ -5640,6 +5688,34 @@ } } }, + "node_modules/vite/node_modules/fdir": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", + "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/public/assets/css/cadastro.css b/public/assets/css/cadastro.css index ec54634..1299477 100644 --- a/public/assets/css/cadastro.css +++ b/public/assets/css/cadastro.css @@ -9,7 +9,6 @@ body { bottom: 0; left: 0; height: 100%; - z-index: -1; } .container { @@ -19,6 +18,9 @@ body { grid-template-columns: repeat(2, 1fr); grid-gap: 7rem; padding: 0 2rem; + bottom: 0; + left: 8rem; + position: fixed; } .img { diff --git a/public/assets/css/login.css b/public/assets/css/login.css index ebe883f..d92acc5 100644 --- a/public/assets/css/login.css +++ b/public/assets/css/login.css @@ -9,7 +9,6 @@ body{ bottom: 0; left: 0; height: 100%; - z-index: -1; } .container{ @@ -19,6 +18,9 @@ body{ grid-template-columns: repeat(2, 1fr); grid-gap :7rem; padding: 0 2rem; + bottom: 0; + left: 8rem; + position: fixed; } .img{ diff --git a/public/assets/css/performance.css b/public/assets/css/performance.css new file mode 100644 index 0000000..1157184 --- /dev/null +++ b/public/assets/css/performance.css @@ -0,0 +1,157 @@ +.layout { + display: grid; + grid-template-columns: 1fr; + padding: 2.5rem; + place-content: center; +} + +.summary-section h1 { + font-size: 2.5rem; + color: #DBC4F8; + margin-bottom: 20px; +} + +.pilares-section { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + gap: 20px; + justify-content: center; + align-items: center; +} + +.card-pilars{ + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; + background-color: #FDF3D6; + border-radius: 1rem; + padding: 1rem; + text-align: center; + min-width: fit-content; + height: 280px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.card-info { + display: flex; + flex-direction: column; + justify-content: start; + padding: 10px; + gap: 30px; +} + +.card-info img { + display: block; + size: 1rem; + height: 150px; + width: fit-content; + background-size: cover; +} + +.card-info h2 { + font-size: 1.5rem; + color: #333; +} + +.card-graphic { + position: relative; + width: 140px; + height: 160px; + display: flex; + justify-content: center; + align-items: center; +} + +.card-graphic svg { + position: absolute; + width: 140px; + height: 140px; + transform: rotate(-90deg); +} + +.card-graphic .background-circle { + fill: none; + stroke: #e0e0e0; + stroke-width: 10; +} + +.card-graphic .progress-circle { + fill: none; + stroke: #DBC4F8; + stroke-width: 10; + stroke-linecap: round; + stroke-dasharray: 0, 440; + transition: stroke-dasharray 0.3s ease; +} + +.card-graphic .percentage { + position: absolute; + font-size: 1.5rem; + font-weight: bold; + color: #333; +} + +.info{ + display: grid; + grid-template-columns: 1.5fr 1fr; + gap: 20px; + margin-top: 2rem; +} + +/* Inicio da stilização da Tabela de Exercicios*/ + +.exercises-section { + display: flex; + flex-direction: column; + padding: 1rem; + background-color: #f9f9f9; + border-radius: 1rem; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +.exercises-section h2 { + font-size: 2rem; + color: #333; + text-align: center; + margin-bottom: 1rem; +} + +.exercises-table { + width: 100%; + border-collapse: collapse; + text-align: center; + font-size: 1rem; +} + +.exercises-table th, +.exercises-table td { + padding: 10px; + border: 1px solid #ddd; +} + +.exercises-table th { + background-color: #FDF3D6; + color: #333; + font-weight: bold; +} + +.exercises-table td .status-button { + display: inline-block; + padding: 5px 10px; + border-radius: 1rem; + font-size: 0.9rem; + font-weight: bold; + color: #fff; + text-align: center; +} + +.exercises-table td .status-button.completed { + background-color: #4caf50; +} + +.exercises-table td .status-button.incomplete { + background-color: #f44336; +} + + +/* FIm da stilização da Tabela de Exercicios*/ diff --git a/public/assets/css/statistics.css b/public/assets/css/statistics.css new file mode 100644 index 0000000..5dbc392 --- /dev/null +++ b/public/assets/css/statistics.css @@ -0,0 +1,170 @@ +.statistics-container { + font-family: "Poppins", sans-serif; + background-color: #dbc4f8; + padding: 30px; + border-radius: 15px; + text-align: center; + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +.title-container { + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + margin-bottom: 25px; +} + +.title-container h1 { + font-size: 2.5rem; + color: #6a0dad; + text-shadow: 2px 2px #ffffff; +} + +.title h1 { + font-size: 2.5rem; + color: #6a0dad; + /* Dark purple */ + margin-bottom: 25px; + text-shadow: 2px 2px #ffffff; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; +} + +.statistics-grid { + display: flex; + flex-wrap: nowrap; + gap: 25px; + justify-content: center; + overflow-x: auto; + padding: 10px; +} + +.statistics-grid::-webkit-scrollbar { + height: 8px; +} + +.statistics-grid::-webkit-scrollbar-thumb { + background-color: #6a0dad; + /* Dark purple */ + border-radius: 4px; +} + +.statistics-grid::-webkit-scrollbar-track { + background-color: #dbc4f8; +} + +.exercise-card { + background: linear-gradient(135deg, #dbc4f8, #ffb6c1); + /* Light purple to light pink */ + border: 2px solid #ff69b4; + /* Hot pink */ + border-radius: 15px; + padding: 20px; + width: 300px; + box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2); + text-align: center; + transition: transform 0.3s ease, box-shadow 0.3s ease; +} + +.exercise-card:nth-child(2) { + background: linear-gradient(135deg, #dbc4f8, #87cefa); + /* Light purple to sky blue */ + border: 2px solid #4682b4; + /* Steel blue */ +} + +.exercise-card:nth-child(3) { + background: linear-gradient(135deg, #dbc4f8, #98fb98); + /* Light purple to pale green */ + border: 2px solid #32cd32; + /* Lime green */ +} + +.exercise-card:nth-child(4) { + background: linear-gradient(135deg, #dbc4f8, #ffa07a); + /* Light purple to light salmon */ + border: 2px solid #ff4500; + /* Orange red */ +} + +.exercise-card:hover { + transform: translateY(-10px); + box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3); +} + +.exercise-card .exercise-details { + background-color: #ffffff; + border-radius: 10px; + padding: 10px; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.exercise-card p { + font-size: 1.2rem; + color: #4b0082; + /* Indigo */ + margin: 8px 0; +} + +.exercise-title { + font-size: 1.5rem; + color: #6a0dad; + /* Dark purple */ + margin-bottom: 10px; + text-shadow: 1px 1px #ffffff; + display: flex; + align-items: center; + justify-content: center; + font-size: larger; + font-weight: bold; + gap: 5px; +} + +.btn { + background-color: #6a0dad; + /* Dark purple */ + color: white; + font-size: 1.5rem; + padding: 12px 25px; + border: none; + border-radius: 1rem; + cursor: pointer; + margin-top: 30px; + transition: background-color 0.3s ease; +} + +.btn:hover { + background-color: #b19cd9; + /* Medium purple */ +} + +.finish-btn { + background-color: #8a2be2; + /* Blue violet */ +} + +.finish-btn:hover { + background-color: #6a0dad; + /* Dark purple */ +} + +.star-container { + display: flex; + flex-direction: row; + justify-content: center; + gap: 5px; + margin-top: 10px; +} + +.star-icon { + width: 20px; + height: 20px; +} \ No newline at end of file diff --git a/src/api/User.jsx b/src/api/User.jsx new file mode 100644 index 0000000..4598400 --- /dev/null +++ b/src/api/User.jsx @@ -0,0 +1,34 @@ +export async function login(email, password) { + const response = await fetch("http://localhost:3000/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }); + + if (!response.ok) { + throw new Error("Login Falhou!"); + } + + const data = await response.json(); + return data; +}; + +export async function cadastro(nome, anoNascimento, email, senha) { + const response = await fetch("http://localhost:3000/register", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ nome, anoNascimento, email, senha }), + }); + + if (!response.ok) { + throw new Error("Cadastro Falhou!"); + } + + const data = await response.json(); + return data; +}; + diff --git a/src/components/App.jsx b/src/components/App.jsx index 74d5481..39212ed 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -3,17 +3,20 @@ import { createRoot } from "react-dom/client"; import { routeTree } from "../routeTree.gen"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRouter, RouterProvider } from "@tanstack/react-router"; +import { UserProvider } from "../contexts/UserContext"; const router = createRouter({ routeTree }) const queryClient = new QueryClient(); const App = () => { return ( - - - - - + + + + + + + ); }; diff --git a/src/components/Header.jsx b/src/components/Header.jsx index cecc937..59506a4 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,7 +1,9 @@ import { Link } from "@tanstack/react-router"; -import '../../public/assets/css/index.css' +import '../../public/assets/css/index.css'; +import { useUser } from "../contexts/UserContext"; const Header = () => { + const { user } = useUser(); return (
@@ -9,22 +11,27 @@ const Header = () => {
diff --git a/src/contexts/UserContext.jsx b/src/contexts/UserContext.jsx new file mode 100644 index 0000000..24a72ad --- /dev/null +++ b/src/contexts/UserContext.jsx @@ -0,0 +1,41 @@ +import { createContext, useContext, useState } from "react" + +//creating a context to store the user data with a default value as null and no function +const UserContext = createContext({ + user: null, + armazenarLogin: (data) => { UserProvider.armazenarLogin(data) }, + armazenarCadastro: (data) => { UserProvider.armazenarCadastro(data) }, + sair: () => { UserProvider.sair() }, +}) + +export const UserProvider = ({ children }) => { + const [user, setUser] = useState(null) + + const armazenarLogin = (data) => { + if (data !== null) { + setUser(data.user) + console.log(user); + } + } + + const armazenarCadastro = (data) => { + if (data !== null) { + setUser(data.user) + console.log(user); + } + } + + const sair = () => { + if (user !== null) { + setUser(null) + } + } + + return ( + + {children} + + ) +} + +export const useUser = () => useContext(UserContext) \ No newline at end of file diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index 9f84467..b6df041 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -1,87 +1,155 @@ import { Link } from '@tanstack/react-router'; import '../../public/assets/css/cadastro.css' +import { useMutation } from '@tanstack/react-query'; +import { cadastro } from '../api/User'; +import { useState } from 'react'; +import { useUser } from '../contexts/UserContext'; const Cadastro = () => { + const [nome, setNome] = useState(''); + const [anoNascimento, setAnoNascimento] = useState(''); + const [email, setEmail] = useState(''); + const [senha, setSenha] = useState(''); + const { armazenarCadastro } = useUser() + + const handleAnoNascimentoChange = (e) => { + let input = e.target.value.replace(/\D/g, ""); // Remove non-numeric characters + if (input.length > 8) { + input = input.substring(0, 8); // Limit to 8 digits + } + if (input.length > 4) { + input = input.replace(/^(\d{2})(\d{2})(\d+)/, "$1/$2/$3"); // Format as DD/MM/YYYY + } else if (input.length > 2) { + input = input.replace(/^(\d{2})(\d+)/, "$1/$2"); // Format as DD/MM + } + setAnoNascimento(input); + }; + + const mutation = useMutation({ + mutationKey: ['cadastro'], + mutationFn: async ({ nome, anoNascimento, email, senha }) => { + cadastro(nome, anoNascimento, email, senha) + }, + onError: (e) => console.log(e), + onSuccess: (data) => { + armazenarCadastro(data) + console.log('Cadastro realizado com sucesso!', data); + }, + }) + return ( <> wave
- grupoMascotes + grupoMascotes
-
-
-

Cadastro

-
-
- -
-
-
Nome Completo
- -
-
-
-
- -
-
-
Ano de Nascimento
- + {mutation.isSuccess ? ( + alert('Cadastro realizado com sucesso!') + ) : ( +
+ mutation.mutate({ nome, anoNascimento, email, senha })}> +

Cadastro

+
+
+ +
+
+
Nome Completo
+ setNome(e.target.value)} + required + minLength="3" + maxLength="50" + title="O nome deve conter apenas letras e espaços" + /> +
-
-
-
- +
+
+ +
+
+
Ano de Nascimento
+ +
-
-
E-mail dos Pais
- +
+
+ +
+
+
E-mail dos Pais
+ setEmail(e.target.value)} + required + minLength="5" + maxLength="50" + title="Digite um e-mail válido" + /> +
-
-
-
- +
+
+ +
+
+
Senha
+ +
-
-
Senha
- +
+
+ +
+
+
Confirmar Senha
+ setSenha(e.target.value)} + required + minLength="8" + maxLength="20" + title="A senha deve ter pelo menos 8 caracteres, incluindo letras e números" + /> +
-
-
-
- +
+
-
-
Confirmar Senha
- -
-
-
- -
-

- Já tem uma conta?Faça login! -

- -
+

+ Já tem uma conta?Faça login! +

+ +
+ )}
- ); }; -export default Cadastro; - - -{/* */} - +export default Cadastro; \ No newline at end of file diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 6724370..b04d6cd 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,7 +1,27 @@ import { Link } from '@tanstack/react-router'; import '../../public/assets/css/login.css' +import { useState } from 'react'; +import { useMutation } from '@tanstack/react-query'; +import { login } from '../api/User'; +import { useUser } from '../contexts/UserContext'; const Login = () => { + const [email, setEmail] = useState(''); + const [senha, setSenha] = useState(''); + const { armazenarLogin } = useUser() + + const mutation = useMutation({ + mutationKey: ['login'], + mutationFn: async ({ email, senha }) => { + login(email, senha) + }, + onError: (e) => console.log(e), + onSuccess: (data) => { + armazenarLogin(data) + console.log('Cadastro realizado com sucesso!', data); + }, + }) + return ( <> wave @@ -10,7 +30,7 @@ const Login = () => { grupoMascotes
-
+ mutation.mutate} >

Login

@@ -18,7 +38,17 @@ const Login = () => {
E-mail
- + setEmail(e.target.value)} + required + minLength="5" + maxLength="50" + pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" + title="Digite um e-mail válido" + />
@@ -27,7 +57,17 @@ const Login = () => {
Senha
- + setSenha(e.target.value)} + required + minLength="8" + maxLength="20" + pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$" + title="A senha deve ter pelo menos 8 caracteres, incluindo letras e números" + />
Esqueceu a sua senha? diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 26d8940..cbcb938 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -18,6 +18,7 @@ import { Route as rootRoute } from './routes/__root' const StatisticsLazyImport = createFileRoute('/statistics')() const RegisterLazyImport = createFileRoute('/register')() +const PerformanceLazyImport = createFileRoute('/performance')() const LoginLazyImport = createFileRoute('/login')() const HomeLazyImport = createFileRoute('/home')() const IndexLazyImport = createFileRoute('/')() @@ -37,6 +38,12 @@ const RegisterLazyRoute = RegisterLazyImport.update({ getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/register.lazy').then((d) => d.Route)) +const PerformanceLazyRoute = PerformanceLazyImport.update({ + id: '/performance', + path: '/performance', + getParentRoute: () => rootRoute, +} as any).lazy(() => import('./routes/performance.lazy').then((d) => d.Route)) + const LoginLazyRoute = LoginLazyImport.update({ id: '/login', path: '/login', @@ -86,6 +93,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LoginLazyImport parentRoute: typeof rootRoute } + '/performance': { + id: '/performance' + path: '/performance' + fullPath: '/performance' + preLoaderRoute: typeof PerformanceLazyImport + parentRoute: typeof rootRoute + } '/register': { id: '/register' path: '/register' @@ -116,6 +130,7 @@ export interface FileRoutesByFullPath { '/': typeof IndexLazyRoute '/home': typeof HomeLazyRoute '/login': typeof LoginLazyRoute + '/performance': typeof PerformanceLazyRoute '/register': typeof RegisterLazyRoute '/statistics': typeof StatisticsLazyRoute '/exercise/$id': typeof ExerciseIdLazyRoute @@ -125,6 +140,7 @@ export interface FileRoutesByTo { '/': typeof IndexLazyRoute '/home': typeof HomeLazyRoute '/login': typeof LoginLazyRoute + '/performance': typeof PerformanceLazyRoute '/register': typeof RegisterLazyRoute '/statistics': typeof StatisticsLazyRoute '/exercise/$id': typeof ExerciseIdLazyRoute @@ -135,6 +151,7 @@ export interface FileRoutesById { '/': typeof IndexLazyRoute '/home': typeof HomeLazyRoute '/login': typeof LoginLazyRoute + '/performance': typeof PerformanceLazyRoute '/register': typeof RegisterLazyRoute '/statistics': typeof StatisticsLazyRoute '/exercise/$id': typeof ExerciseIdLazyRoute @@ -146,16 +163,25 @@ export interface FileRouteTypes { | '/' | '/home' | '/login' + | '/performance' | '/register' | '/statistics' | '/exercise/$id' fileRoutesByTo: FileRoutesByTo - to: '/' | '/home' | '/login' | '/register' | '/statistics' | '/exercise/$id' + to: + | '/' + | '/home' + | '/login' + | '/performance' + | '/register' + | '/statistics' + | '/exercise/$id' id: | '__root__' | '/' | '/home' | '/login' + | '/performance' | '/register' | '/statistics' | '/exercise/$id' @@ -166,6 +192,7 @@ export interface RootRouteChildren { IndexLazyRoute: typeof IndexLazyRoute HomeLazyRoute: typeof HomeLazyRoute LoginLazyRoute: typeof LoginLazyRoute + PerformanceLazyRoute: typeof PerformanceLazyRoute RegisterLazyRoute: typeof RegisterLazyRoute StatisticsLazyRoute: typeof StatisticsLazyRoute ExerciseIdLazyRoute: typeof ExerciseIdLazyRoute @@ -175,6 +202,7 @@ const rootRouteChildren: RootRouteChildren = { IndexLazyRoute: IndexLazyRoute, HomeLazyRoute: HomeLazyRoute, LoginLazyRoute: LoginLazyRoute, + PerformanceLazyRoute: PerformanceLazyRoute, RegisterLazyRoute: RegisterLazyRoute, StatisticsLazyRoute: StatisticsLazyRoute, ExerciseIdLazyRoute: ExerciseIdLazyRoute, @@ -193,6 +221,7 @@ export const routeTree = rootRoute "/", "/home", "/login", + "/performance", "/register", "/statistics", "/exercise/$id" @@ -207,6 +236,9 @@ export const routeTree = rootRoute "/login": { "filePath": "login.lazy.jsx" }, + "/performance": { + "filePath": "performance.lazy.jsx" + }, "/register": { "filePath": "register.lazy.jsx" }, diff --git a/src/routes/performance.lazy.jsx b/src/routes/performance.lazy.jsx new file mode 100644 index 0000000..267b7bb --- /dev/null +++ b/src/routes/performance.lazy.jsx @@ -0,0 +1,107 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import "../../public/assets/css/performance.css"; +import Header from "../components/Header"; + +export const Route = createLazyFileRoute("/performance")({ + component: PerformanceComponent, +}); + +function PerformanceComponent() { + const pillars = [ + { name: "Decomposição", img: "/img/ana.png", lessons: 8, total: 10 }, + { + name: "Reconhecimento de Padrões", + img: "/img/lilu.png", + lessons: 6, + total: 10, + }, + { name: "Abstração", img: "/img/soso.png", lessons: 7, total: 10 }, + { name: "Algoritmos", img: "/img/cadu.png", lessons: 9, total: 10 }, + ]; + + const exercises = [ + { name: "Exercício 1", time: "10m", tries: 2, completed: true }, + { name: "Exercício 2", time: "15m", tries: 3, completed: false }, + { name: "Exercício 3", time: "12m", tries: 1, completed: true }, + { name: "Exercício 4", time: "8m", tries: 1, completed: true }, + ]; + + return ( + <> +
+
+
+

Resumo

+
+
+ {pillars.map((pillar, index) => ( +
+
+ {pillar.name} +

{pillar.name}

+
+
+ + + + +
+ {`${pillar.lessons}/${pillar.total}`} +
+
+
+ ))} +
+
+
+

Exercícios

+ + + + + + + + + + + {exercises.map((exercise, index) => ( + + + + + + + ))} + +
NomeTempoTentativasStatus
{exercise.name}{exercise.time}{exercise.tries} + + {exercise.completed ? "Concluído" : "Não Concluído"} + +
+
+
+
+ + ); +} diff --git a/src/routes/statistics.lazy.jsx b/src/routes/statistics.lazy.jsx index 07a8df1..50418db 100644 --- a/src/routes/statistics.lazy.jsx +++ b/src/routes/statistics.lazy.jsx @@ -1,5 +1,5 @@ import { createLazyFileRoute, Link } from "@tanstack/react-router"; -import "../../public/assets/css/exercicio1.css"; +import "../../public/assets/css/statistics.css"; export const Route = createLazyFileRoute("/statistics")({ component: statisticsComponent, @@ -8,74 +8,118 @@ export const Route = createLazyFileRoute("/statistics")({ function statisticsComponent() { const keys = Object.keys(localStorage).filter(key => key.startsWith('Exer')); const todosExer = keys.map(key => JSON.parse(localStorage.getItem(key))); - + + const Star = () => ( + + + + ); + return ( <> -
+
{console.log(localStorage)} {console.log(todosExer[3][3])}
-
-

- Estatísticas: -

-

- Exercicio 1 -

-

- Exercicio Concluido: {todosExer[0][0]} -

-

- Tentativas: {todosExer[0][1]} -

-

- Tempo de Conclusão: {todosExer[0][2]} segundos -

+
+

Estatísticas

+
+ +
+
+
+
+

+ Exercicio 1 +

+
+

+ Exercicio Concluido: {todosExer[0][0]} +

+

+ Tentativas: {todosExer[0][1]} +

+

+ Tempo de Conclusão: {todosExer[0][2]} segundos +

+
+
+ +
+
-

- Exercicio 2 -

-

- Exercicio Concluido: {todosExer[1][0]} -

-

- Tentativas: {todosExer[1][1]} -

-

- Tempo de Conclusão: {todosExer[1][2]} segundos -

+
+

+ Exercicio 2 +

+
+

+ Exercicio Concluido: {todosExer[1][0]} +

+

+ Tentativas: {todosExer[1][1]} +

+

+ Tempo de Conclusão: {todosExer[1][2]} segundos +

+
+
+ +
+
-

- Exercicio 3 -

-

- Tempo de Conclusão: {todosExer[2][0]} -

-

- Tentativas: {todosExer[2][1]} -

-

- Tempo Total: {todosExer[2][2]} segundos -

+
+

+ Exercicio 3 +

+
+

+ Tempo de Conclusão: {todosExer[2][0]} +

+

+ Tentativas: {todosExer[2][1]} +

+

+ Tempo Total: {todosExer[2][2]} segundos +

+
+
+ +
+
-

- Exercicio 4 -

-

- Exercicio Concluido: {todosExer[3][0]} -

-

- Tentativas: {todosExer[3][1]} -

-

- Tempo de Conclusão: {todosExer[3][2]} segundos -

- - - +
+

+ Exercicio 4 +

+
+

+ Exercicio Concluido: {todosExer[3][0]} +

+

+ Tentativas: {todosExer[3][1]} +

+

+ Tempo de Conclusão: {todosExer[3][2]} segundos +

+
+
+ +
+
+ + +
diff --git a/vite.config.js b/vite.config.js index f6d9d5b..2b9bf92 100644 --- a/vite.config.js +++ b/vite.config.js @@ -3,19 +3,13 @@ import react from "@vitejs/plugin-react"; import { TanStackRouterVite } from "@tanstack/router-plugin/vite"; export default defineConfig({ - plugins: [ - TanStackRouterVite(), - react({ - babel: { - plugins: [ - [ - "babel-plugin-react-compiler", - { - target: "19", - }, - ], - ], + plugins: [TanStackRouterVite(), react()], + server: { + proxy: { + '/api': { + target: 'http://localhost:3000', + changeOrigin: true, }, - }), - ], + }, + }, }); From def2d0ba9f6576ea4138bec67036ab0bf9ba9c1c Mon Sep 17 00:00:00 2001 From: ArthurVenturi Date: Thu, 17 Apr 2025 20:02:53 -0300 Subject: [PATCH 02/10] Adicionando popUp simples de Sucesso --- public/assets/css/successPopUp.css | 37 +++++++ src/pages/Cadastro.jsx | 3 +- src/pages/Login.jsx | 164 +++++++++++++++-------------- src/pages/successPopUp.jsx | 16 +++ 4 files changed, 140 insertions(+), 80 deletions(-) create mode 100644 public/assets/css/successPopUp.css create mode 100644 src/pages/successPopUp.jsx diff --git a/public/assets/css/successPopUp.css b/public/assets/css/successPopUp.css new file mode 100644 index 0000000..51d107e --- /dev/null +++ b/public/assets/css/successPopUp.css @@ -0,0 +1,37 @@ +.popup-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.popup-content { + background-color: #fff; + padding: 20px; + border-radius: 8px; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + text-align: center; + max-width: 400px; + width: 90%; +} + +.close-button { + margin-top: 10px; + padding: 10px 20px; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 16px; +} + +.close-button:hover { + background-color: #45a049; +} diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index b6df041..2d65b0f 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -4,6 +4,7 @@ import { useMutation } from '@tanstack/react-query'; import { cadastro } from '../api/User'; import { useState } from 'react'; import { useUser } from '../contexts/UserContext'; +import SuccessPopUp from './successPopUp'; const Cadastro = () => { const [nome, setNome] = useState(''); @@ -45,7 +46,7 @@ const Cadastro = () => { grupoMascotes
{mutation.isSuccess ? ( - alert('Cadastro realizado com sucesso!') + ) : (
mutation.mutate({ nome, anoNascimento, email, senha })}> diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index b04d6cd..81dc8a8 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,86 +1,92 @@ -import { Link } from '@tanstack/react-router'; -import '../../public/assets/css/login.css' -import { useState } from 'react'; -import { useMutation } from '@tanstack/react-query'; -import { login } from '../api/User'; -import { useUser } from '../contexts/UserContext'; +import { Link } from "@tanstack/react-router"; +import "../../public/assets/css/login.css"; +import { useState } from "react"; +import { useMutation } from "@tanstack/react-query"; +import { login } from "../api/User"; +import { useUser } from "../contexts/UserContext"; +import SuccessPopUp from "../components/successPopUp"; const Login = () => { - const [email, setEmail] = useState(''); - const [senha, setSenha] = useState(''); - const { armazenarLogin } = useUser() + const [email, setEmail] = useState(""); + const [senha, setSenha] = useState(""); + const { armazenarLogin } = useUser(); - const mutation = useMutation({ - mutationKey: ['login'], - mutationFn: async ({ email, senha }) => { - login(email, senha) - }, - onError: (e) => console.log(e), - onSuccess: (data) => { - armazenarLogin(data) - console.log('Cadastro realizado com sucesso!', data); - }, - }) + const mutation = useMutation({ + mutationKey: ["login"], + mutationFn: async ({ email, senha }) => { + login(email, senha); + }, + onError: (e) => console.log(e), + onSuccess: (data) => { + armazenarLogin(data); + console.log("Cadastro realizado com sucesso!", data); + }, + }); - return ( - <> - wave -
-
- grupoMascotes -
-
- mutation.mutate} > -

Login

-
-
- -
-
-
E-mail
- setEmail(e.target.value)} - required - minLength="5" - maxLength="50" - pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" - title="Digite um e-mail válido" - /> -
-
-
-
- -
-
-
Senha
- setSenha(e.target.value)} - required - minLength="8" - maxLength="20" - pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$" - title="A senha deve ter pelo menos 8 caracteres, incluindo letras e números" - /> -
-
- Esqueceu a sua senha? -
- -
-

Não tem uma conta? Crie uma!

- -
-
- - ); + return ( + <> + wave +
+
+ grupoMascotes +
+ {mutation.isSuccess ? ( + + ) : ( +
+
mutation.mutate}> +

Login

+
+
+ +
+
+
E-mail
+ setEmail(e.target.value)} + required + minLength="5" + maxLength="50" + pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" + title="Digite um e-mail válido" + /> +
+
+
+
+ +
+
+
Senha
+ setSenha(e.target.value)} + required + minLength="8" + maxLength="20" + pattern="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$" + title="A senha deve ter pelo menos 8 caracteres, incluindo letras e números" + /> +
+
+ Esqueceu a sua senha? +
+ +
+

+ Não tem uma conta? Crie uma! +

+
+
+ )} +
+ + ); }; export default Login; - diff --git a/src/pages/successPopUp.jsx b/src/pages/successPopUp.jsx new file mode 100644 index 0000000..7fa6ec7 --- /dev/null +++ b/src/pages/successPopUp.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import '../../public/assets/css/successPopUp.css'; +import { Link } from '@tanstack/react-router'; + +const SuccessPopUp = ({ message }) => { + return ( +
+
+

{message}

+ Ir para a Home +
+
+ ); +}; + +export default SuccessPopUp; From b4009a137850d62ba2533e0166a8450fb9c81ee0 Mon Sep 17 00:00:00 2001 From: ArthurVenturi Date: Wed, 4 Jun 2025 22:22:11 -0300 Subject: [PATCH 03/10] =?UTF-8?q?Adi=C3=A7=C3=A3o=20da=20p=C3=A1gina=20404?= =?UTF-8?q?=20e=20captura=20de=20error=20como=20ajustes=20na=20p=C3=A1gina?= =?UTF-8?q?=20performance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 48 +++-- package.json | 2 +- public/assets/css/header.css | 58 ++++++ public/assets/css/index.css | 4 +- public/assets/css/performance.css | 51 ++++- public/assets/css/statistics.css | 183 ++++++----------- public/assets/css/utilidades.css | 77 ++++++++ src/api/User.jsx | 4 +- src/components/ErrorBoundaries.jsx | 79 ++++++++ src/components/Header.jsx | 88 +++++---- src/{pages => components}/successPopUp.jsx | 3 +- src/pages/Cadastro.jsx | 19 +- src/pages/Login.jsx | 4 +- src/routes/__root.jsx | 22 ++- src/routes/performance.lazy.jsx | 45 ++++- src/routes/statistics.lazy.jsx | 217 ++++++++++++--------- 16 files changed, 580 insertions(+), 324 deletions(-) create mode 100644 public/assets/css/header.css create mode 100644 public/assets/css/utilidades.css create mode 100644 src/components/ErrorBoundaries.jsx rename src/{pages => components}/successPopUp.jsx (87%) diff --git a/package-lock.json b/package-lock.json index 87e11f4..0bf3a7e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@tanstack/react-router": "^1.97.1", "jquery": "^3.7.1", "jquery-touchswipe": "^1.6.19", + "primeicons": "^7.0.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -21,7 +22,6 @@ "@tanstack/router-plugin": "^1.97.1", "@testing-library/react": "^16.2.0", "@vitejs/plugin-react": "^4.3.4", - "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint": "^9.18.0", "eslint-plugin-react": "^7.37.4", "globals": "^15.14.0", @@ -2187,16 +2187,6 @@ "@babel/types": "^7.23.6" } }, - "node_modules/babel-plugin-react-compiler": { - "version": "19.0.0-beta-e552027-20250112", - "resolved": "https://registry.npmjs.org/babel-plugin-react-compiler/-/babel-plugin-react-compiler-19.0.0-beta-e552027-20250112.tgz", - "integrity": "sha512-pUTT0mAZ4XLewC6bvqVeX015nVRLVultcSQlkzGdC10G6YV6K2h4E7cwGlLAuLKWTj3Z08mTO9uTnPP/opUBsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.19.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4690,6 +4680,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5334,13 +5330,13 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2" }, "engines": { @@ -5351,9 +5347,9 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5614,18 +5610,18 @@ } }, "node_modules/vite": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz", - "integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.3", + "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", - "tinyglobby": "^0.2.12" + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -5689,9 +5685,9 @@ } }, "node_modules/vite/node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", "dev": true, "license": "MIT", "peerDependencies": { diff --git a/package.json b/package.json index a07e075..d841c43 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "@tanstack/react-router": "^1.97.1", "jquery": "^3.7.1", "jquery-touchswipe": "^1.6.19", + "primeicons": "^7.0.0", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -35,7 +36,6 @@ "@tanstack/router-plugin": "^1.97.1", "@testing-library/react": "^16.2.0", "@vitejs/plugin-react": "^4.3.4", - "babel-plugin-react-compiler": "^19.0.0-beta-e552027-20250112", "eslint": "^9.18.0", "eslint-plugin-react": "^7.37.4", "globals": "^15.14.0", diff --git a/public/assets/css/header.css b/public/assets/css/header.css new file mode 100644 index 0000000..17a1f2e --- /dev/null +++ b/public/assets/css/header.css @@ -0,0 +1,58 @@ +.header-container { + display: flex; + justify-content: flex-end; + align-items: center; + padding: 14px 32px; + background-color: #d6f6de; + box-shadow: 0 4px 12px rgba(0,0,0,0.04); + box-sizing: border-box; +} + +.header-nav { + display: flex; + align-items: center; +} + +.main-nav { + display: flex; + align-items: center; +} + +.logo { + width: 48px; + height: 48px; + background: url('/assets/img/logo.png') no-repeat center center; + background-size: contain; + margin-right: 24px; +} + +.flex-container { + display: flex; + gap: 18px; + list-style: none; + margin: 0; + padding: 0; +} + +.scroll-to-section { + display: flex; + align-items: center; +} + +.login-button { + color: #fff; + border-radius: 6px; + padding: 6px 18px; + transition: background 0.2s; +} + +.login-button a:hover { + color: black !important; +} + +.border-first-button { + border-radius: 6px; + padding: 6px 18px; + margin-left: 8px; + transition: background 0.2s, color 0.2s; +} \ No newline at end of file diff --git a/public/assets/css/index.css b/public/assets/css/index.css index adc9027..ae3420e 100644 --- a/public/assets/css/index.css +++ b/public/assets/css/index.css @@ -51,11 +51,11 @@ body { a { text-decoration: none !important; - color: #ffbe98; /* Laranja Claro */ + color: white; /* Laranja Claro */ } a:hover { - color: #f9b2bc; /* Rosa Claro */ + color: black; /* Rosa Claro */ } h1, diff --git a/public/assets/css/performance.css b/public/assets/css/performance.css index 1157184..bcd642a 100644 --- a/public/assets/css/performance.css +++ b/public/assets/css/performance.css @@ -1,8 +1,12 @@ .layout { - display: grid; - grid-template-columns: 1fr; - padding: 2.5rem; + display: flex; + flex-direction: column; + padding: 1rem; place-content: center; + box-sizing: border-box; + width: 100vw; + min-height: 100vh; + max-width: 100vw; } .summary-section h1 { @@ -13,10 +17,11 @@ .pilares-section { display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr; - gap: 20px; - justify-content: center; - align-items: center; + grid-template-columns: repeat(4, 1fr); + gap: 10px; + width: 100%; + justify-content: stretch; + align-items: stretch; } .card-pilars{ @@ -30,6 +35,7 @@ min-width: fit-content; height: 280px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + position: relative; } .card-info { @@ -54,7 +60,7 @@ } .card-graphic { - position: relative; + position: absolute; width: 140px; height: 160px; display: flex; @@ -153,5 +159,30 @@ background-color: #f44336; } - -/* FIm da stilização da Tabela de Exercicios*/ +@media (max-width: 900px) { + .pilares-section { + grid-template-columns: 1fr 1fr; + } + .info { + grid-template-columns: 1fr; + } +} + +@media (max-width: 600px) { + .layout { + padding: 1rem; + gap: 1rem; + } + .pilares-section { + grid-template-columns: 1fr; + gap: 10px; + } + .card-pilars { + height: auto; + grid-template-columns: 1fr; + } + .info { + grid-template-columns: 1fr; + gap: 10px; + } +} diff --git a/public/assets/css/statistics.css b/public/assets/css/statistics.css index 5dbc392..e82ab66 100644 --- a/public/assets/css/statistics.css +++ b/public/assets/css/statistics.css @@ -1,170 +1,101 @@ .statistics-container { - font-family: "Poppins", sans-serif; - background-color: #dbc4f8; - padding: 30px; - border-radius: 15px; - text-align: center; - box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; align-items: center; justify-content: center; + padding: 20px; min-height: 100vh; } -.title-container { +.statistics-content { display: flex; - flex-direction: column; align-items: center; - gap: 10px; - margin-bottom: 25px; + justify-content: center; + padding: 20px; + margin: 0 25px 32px 25px; } -.title-container h1 { - font-size: 2.5rem; - color: #6a0dad; - text-shadow: 2px 2px #ffffff; +.points-container { + position: absolute; + top: 20px; + right: -25px; } -.title h1 { - font-size: 2.5rem; - color: #6a0dad; - /* Dark purple */ - margin-bottom: 25px; - text-shadow: 2px 2px #ffffff; +.points { display: flex; + gap: 8px; + background-color: white; + padding: 6px 12px; + border-radius: 1rem; align-items: center; - justify-content: center; - gap: 10px; + box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1); } -.statistics-grid { +.title-container { display: flex; - flex-wrap: nowrap; - gap: 25px; + flex-direction: column; + align-items: center; justify-content: center; - overflow-x: auto; - padding: 10px; } -.statistics-grid::-webkit-scrollbar { - height: 8px; -} - -.statistics-grid::-webkit-scrollbar-thumb { - background-color: #6a0dad; - /* Dark purple */ - border-radius: 4px; -} - -.statistics-grid::-webkit-scrollbar-track { - background-color: #dbc4f8; +.statistics-grid { + display: flex; + flex-wrap: wrap; + gap: 20px; + width: 100%; } .exercise-card { - background: linear-gradient(135deg, #dbc4f8, #ffb6c1); - /* Light purple to light pink */ - border: 2px solid #ff69b4; - /* Hot pink */ - border-radius: 15px; - padding: 20px; - width: 300px; - box-shadow: 0 6px 10px rgba(0, 0, 0, 0.2); - text-align: center; - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.exercise-card:nth-child(2) { - background: linear-gradient(135deg, #dbc4f8, #87cefa); - /* Light purple to sky blue */ - border: 2px solid #4682b4; - /* Steel blue */ -} - -.exercise-card:nth-child(3) { - background: linear-gradient(135deg, #dbc4f8, #98fb98); - /* Light purple to pale green */ - border: 2px solid #32cd32; - /* Lime green */ -} - -.exercise-card:nth-child(4) { - background: linear-gradient(135deg, #dbc4f8, #ffa07a); - /* Light purple to light salmon */ - border: 2px solid #ff4500; - /* Orange red */ -} - -.exercise-card:hover { - transform: translateY(-10px); - box-shadow: 0 8px 15px rgba(0, 0, 0, 0.3); -} - -.exercise-card .exercise-details { - background-color: #ffffff; - border-radius: 10px; - padding: 10px; - box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; + background-color: white; + padding: 40px; + border-radius: 1rem; + position: relative; } -.exercise-card p { - font-size: 1.2rem; - color: #4b0082; - /* Indigo */ - margin: 8px 0; +.exercise-details { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px; } -.exercise-title { - font-size: 1.5rem; - color: #6a0dad; - /* Dark purple */ - margin-bottom: 10px; - text-shadow: 1px 1px #ffffff; +.data { display: flex; + flex-direction: column; align-items: center; justify-content: center; - font-size: larger; - font-weight: bold; - gap: 5px; + gap: 10px; + padding: 10px; + border-radius: 5px; } -.btn { - background-color: #6a0dad; - /* Dark purple */ - color: white; +.data li { font-size: 1.5rem; - padding: 12px 25px; - border: none; - border-radius: 1rem; - cursor: pointer; - margin-top: 30px; - transition: background-color 0.3s ease; } -.btn:hover { - background-color: #b19cd9; - /* Medium purple */ +progress{ + height: 10px; + appearance: none; } -.finish-btn { - background-color: #8a2be2; - /* Blue violet */ +progress[value]::-webkit-progress-bar { + background-color: #f0f0f0; + border-radius: 1rem; } -.finish-btn:hover { - background-color: #6a0dad; - /* Dark purple */ +progress[value]::-webkit-progress-value { + background-color: #dbc4f8; + border-radius: 1rem; } -.star-container { - display: flex; - flex-direction: row; - justify-content: center; - gap: 5px; - margin-top: 10px; +.finish-btn{ + background-color: #dbc4f8; + color: white; + padding: 10px 20px; + border: none; + border-radius: 5px; + cursor: pointer; } - -.star-icon { - width: 20px; - height: 20px; -} \ No newline at end of file diff --git a/public/assets/css/utilidades.css b/public/assets/css/utilidades.css new file mode 100644 index 0000000..f56370f --- /dev/null +++ b/public/assets/css/utilidades.css @@ -0,0 +1,77 @@ +.error-boundary-fallback { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100vw; + height: 100vh; + color: #C197F5; +} + +.error-boundary-content { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + gap: 1rem; + max-width: 600px; + padding: 20px; +} + +.exception-button{ + background-color: white; + color: var(--primary-color); + font-size: large; + font-weight: bolder; + padding: 10px 20px; + border-radius: 2rem; + border: 1px solid #C197F5; +} + +.exception-button:hover { + background-color: #C197F5; + color: white; +} + +.error-boundary-content h1 { + font-size: 8rem; +} + +.error-boundary-content h2 { + font-size: 1.5rem; +} + +/*End of error-boundary-fallback styles*/ + +.container-404 { + text-align: center; + margin-top: 10%; +} + +.container-404 h1 { + font-size: 6rem; + margin: 20px 0; +} + +.container-404 p { + font-size: 1.5rem; + margin: 25px 0; +} + +.container-404 .home-button { + display: inline-block; + padding: 15px 25px; + font-size: 1rem; + color: #C197F5; + text-decoration: none; + border: 1px solid #C197F5; + border-radius: 1rem; + transition: background-color 0.3s ease; +} + +.container-404 .home-button:hover { + background-color: #C197F5; + color: #fff; +} + +/*End of 404 page styles*/ \ No newline at end of file diff --git a/src/api/User.jsx b/src/api/User.jsx index 4598400..264f2ae 100644 --- a/src/api/User.jsx +++ b/src/api/User.jsx @@ -5,7 +5,7 @@ export async function login(email, password) { "Content-Type": "application/json", }, body: JSON.stringify({ email, password }), - }); + }).catch((e) => {throw new Error("Erro de conexão!", e)}); if (!response.ok) { throw new Error("Login Falhou!"); @@ -22,7 +22,7 @@ export async function cadastro(nome, anoNascimento, email, senha) { "Content-Type": "application/json", }, body: JSON.stringify({ nome, anoNascimento, email, senha }), - }); + }).catch((e) => {throw new Error("Erro de conexão!", e)}); if (!response.ok) { throw new Error("Cadastro Falhou!"); diff --git a/src/components/ErrorBoundaries.jsx b/src/components/ErrorBoundaries.jsx new file mode 100644 index 0000000..428a05d --- /dev/null +++ b/src/components/ErrorBoundaries.jsx @@ -0,0 +1,79 @@ +import * as React from "react"; +import "../../public/assets/css/utilidades.css"; +import { useNavigate } from "@tanstack/react-router"; + +// ErrorBoundary catches JavaScript errors in its child component tree, logs them, and displays a fallback UI. +// Use it to prevent the entire app from crashing due to errors in part of the UI. +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + this.handleGoHome = this.handleGoHome.bind(this); + } + + static getDerivedStateFromError(error) { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + componentDidCatch(error, info) { + this.setState({ hasError: true }); + console.log(error, info); + // You can also log the error to an error reporting service here. + } + + handleGoHome() { + if (this.props.navigate) { + this.props.navigate({ to: "/" }); + setTimeout(() => window.location.reload(), 0); + } + } + + render() { + if (this.state.hasError) { + // Render custom fallback UI if provided, otherwise show a default message. + return ( + this.props.fallback || ( +
+
+

WHOPPS!

+

Alguma coisa deu errado. Isso é constrangedor

+ +
+
+ ) + ); + } + + return this.props.children; + } +} + +// Wrapper to inject navigate prop from useNavigate into ErrorBoundary +export function ErrorBoundaryWithNavigate(props) { + const navigate = useNavigate(); + return ; +} + +export const Pagina404 = () => { + const navigate = useNavigate(); + + const handleGoHome = () => { + navigate({ to: "/" }); + setTimeout(() => window.location.reload(), 0); + }; + + return ( +
+

404 - Página não encontrada

+

Desculpe, a página que você está procurando não existe.

+ +
+ ); +}; \ No newline at end of file diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 59506a4..07e6900 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,43 +1,59 @@ import { Link } from "@tanstack/react-router"; -import '../../public/assets/css/index.css'; +import "../../public/assets/css/header.css"; import { useUser } from "../contexts/UserContext"; +// import "../../public/assets/css/index.css"; const Header = () => { - const { user } = useUser(); - return ( -
-
-
-
- -
+ const { user } = useUser(); + return ( +
+
+
-
- ); + + ) : ( + <> +
  • +
    + Login +
    +
  • +
  • +
    + Criar conta +
    +
  • + + )} + + +
    +
    + ); }; -export default Header; \ No newline at end of file +export default Header; diff --git a/src/pages/successPopUp.jsx b/src/components/successPopUp.jsx similarity index 87% rename from src/pages/successPopUp.jsx rename to src/components/successPopUp.jsx index 7fa6ec7..41eeb27 100644 --- a/src/pages/successPopUp.jsx +++ b/src/components/successPopUp.jsx @@ -1,4 +1,3 @@ -import React from 'react'; import '../../public/assets/css/successPopUp.css'; import { Link } from '@tanstack/react-router'; @@ -13,4 +12,4 @@ const SuccessPopUp = ({ message }) => { ); }; -export default SuccessPopUp; +export default SuccessPopUp; \ No newline at end of file diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index 2d65b0f..94f42ad 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -4,7 +4,7 @@ import { useMutation } from '@tanstack/react-query'; import { cadastro } from '../api/User'; import { useState } from 'react'; import { useUser } from '../contexts/UserContext'; -import SuccessPopUp from './successPopUp'; +import SuccessPopUp from '../components/successPopUp'; const Cadastro = () => { const [nome, setNome] = useState(''); @@ -13,23 +13,10 @@ const Cadastro = () => { const [senha, setSenha] = useState(''); const { armazenarCadastro } = useUser() - const handleAnoNascimentoChange = (e) => { - let input = e.target.value.replace(/\D/g, ""); // Remove non-numeric characters - if (input.length > 8) { - input = input.substring(0, 8); // Limit to 8 digits - } - if (input.length > 4) { - input = input.replace(/^(\d{2})(\d{2})(\d+)/, "$1/$2/$3"); // Format as DD/MM/YYYY - } else if (input.length > 2) { - input = input.replace(/^(\d{2})(\d+)/, "$1/$2"); // Format as DD/MM - } - setAnoNascimento(input); - }; - const mutation = useMutation({ mutationKey: ['cadastro'], mutationFn: async ({ nome, anoNascimento, email, senha }) => { - cadastro(nome, anoNascimento, email, senha) + cadastro(nome, anoNascimento, email, parseInt(senha)) }, onError: (e) => console.log(e), onSuccess: (data) => { @@ -81,7 +68,7 @@ const Cadastro = () => { id="birthyear" maxLength="10" value={anoNascimento} - onChange={handleAnoNascimentoChange} + onChange={(e) => setAnoNascimento(e.target.value)} required title="Digite uma data válida no formato DD/MM/AAAA" /> diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 81dc8a8..312d04e 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -14,7 +14,7 @@ const Login = () => { const mutation = useMutation({ mutationKey: ["login"], mutationFn: async ({ email, senha }) => { - login(email, senha); + login(email, parseInt(senha)); }, onError: (e) => console.log(e), onSuccess: (data) => { @@ -34,7 +34,7 @@ const Login = () => { ) : (
    -
    mutation.mutate}> + mutation.mutate({ email, senha })}>

    Login

    diff --git a/src/routes/__root.jsx b/src/routes/__root.jsx index 7328b44..2a7434b 100644 --- a/src/routes/__root.jsx +++ b/src/routes/__root.jsx @@ -1,14 +1,18 @@ -import * as React from 'react' -import { createRootRoute, Outlet } from '@tanstack/react-router' +import * as React from "react"; +import { createRootRoute, Outlet, useRouterState } from "@tanstack/react-router"; +import { ErrorBoundaryWithNavigate, Pagina404 }from "../components/ErrorBoundaries"; export const Route = createRootRoute({ - component: RootComponent, -}) + component: RootComponent, +}); function RootComponent() { - return ( - - - - ) + const { statusCode } = useRouterState(); + return ( + + + {statusCode === 404 ? : } + + + ); } diff --git a/src/routes/performance.lazy.jsx b/src/routes/performance.lazy.jsx index 267b7bb..b6de85f 100644 --- a/src/routes/performance.lazy.jsx +++ b/src/routes/performance.lazy.jsx @@ -7,6 +7,23 @@ export const Route = createLazyFileRoute("/performance")({ }); function PerformanceComponent() { + const keys = Object.keys(localStorage).filter((key) => + key.startsWith("Exer") + ); + + // Add static fallback values for testing + const defaultTodosExer = [ + [0, 2, "00:01:30"], + [0, 1, "00:02:10"], + [0, 3, "00:01:50"], + [0, 2, "00:03:00"], + ]; + + const todosExer = + keys.length === 4 + ? keys.map((key) => JSON.parse(localStorage.getItem(key))) + : defaultTodosExer; + const pillars = [ { name: "Decomposição", img: "/img/ana.png", lessons: 8, total: 10 }, { @@ -20,10 +37,30 @@ function PerformanceComponent() { ]; const exercises = [ - { name: "Exercício 1", time: "10m", tries: 2, completed: true }, - { name: "Exercício 2", time: "15m", tries: 3, completed: false }, - { name: "Exercício 3", time: "12m", tries: 1, completed: true }, - { name: "Exercício 4", time: "8m", tries: 1, completed: true }, + { + name: "Organize as Cores do Arco-Íris", + time: todosExer[0][2], + tries: todosExer[0][1], + completed: true, + }, + { + name: "Navegar o labirinto", + time: todosExer[1][2], + tries: 0, + completed: true, + }, + { + name: "Caça ao Tesouro", + time: todosExer[2][2], + tries: 0, + completed: true, + }, + { + name: "Monte o Quebra-Cabeça", + time: todosExer[3][2], + tries: 0, + completed: true, + }, ]; return ( diff --git a/src/routes/statistics.lazy.jsx b/src/routes/statistics.lazy.jsx index 50418db..dd7c4ca 100644 --- a/src/routes/statistics.lazy.jsx +++ b/src/routes/statistics.lazy.jsx @@ -1,21 +1,31 @@ import { createLazyFileRoute, Link } from "@tanstack/react-router"; import "../../public/assets/css/statistics.css"; +import "primeicons/primeicons.css"; export const Route = createLazyFileRoute("/statistics")({ component: statisticsComponent, }); function statisticsComponent() { - const keys = Object.keys(localStorage).filter(key => key.startsWith('Exer')); - const todosExer = keys.map(key => JSON.parse(localStorage.getItem(key))); + const keys = Object.keys(localStorage).filter((key) => + key.startsWith("Exer") + ); + const todosExer = keys.map((key) => JSON.parse(localStorage.getItem(key))); + console.log(todosExer) + + /* + gold:#C9B037 + silver:#B4B4B4 + bronze:#CD7F32 + */ const Star = () => ( @@ -23,105 +33,136 @@ function statisticsComponent() { ); return ( - <> -
    - {console.log(localStorage)} - {console.log(todosExer[3][3])} -
    -
    -

    Estatísticas

    +
    +
    +

    Parabéns

    +

    Você concluiu todas os exercícios

    +
    +
    +
    +
    - +
    -
    -
    -
    -

    - Exercicio 1 -

    -
    -

    - Exercicio Concluido: {todosExer[0][0]} -

    -

    - Tentativas: {todosExer[0][1]} -

    -

    - Tempo de Conclusão: {todosExer[0][2]} segundos -

    +
    +
    +
  • + {todosExer[0][3]} 50 XP +
    +
    +

    + Exercicio 1 +

    +
    +
    +
  • + {todosExer[0][2]} s
    -
    - +
    +
  • + {todosExer[0][1]}
    - -
    -

    - Exercicio 2 -

    -
    -

    - Exercicio Concluido: {todosExer[1][0]} -

    -

    - Tentativas: {todosExer[1][1]} -

    -

    - Tempo de Conclusão: {todosExer[1][2]} segundos -

    + + Concluido +
    +
    +
    + +
    +
    +
    +
  • + {todosExer[0][3]} 50 XP +
    +
    +

    + Exercicio 2 +

    +
    +
    +
  • + {todosExer[1][2]} s
    -
    - +
    +
  • + {todosExer[1][1]}
    - -
    -

    - Exercicio 3 -

    -
    -

    - Tempo de Conclusão: {todosExer[2][0]} -

    -

    - Tentativas: {todosExer[2][1]} -

    -

    - Tempo Total: {todosExer[2][2]} segundos -

    + + Concluido +
    +
    +
    + +
    +
    +
    +
  • + {todosExer[2][3]} 50 XP +
    +
    +

    + Exercicio 3 +

    +
    +
    +
  • + {todosExer[2][2]} s
    -
    - +
    +
  • + {todosExer[2][1]}
    - -
    -

    - Exercicio 4 -

    -
    -

    - Exercicio Concluido: {todosExer[3][0]} -

    -

    - Tentativas: {todosExer[3][1]} -

    -

    - Tempo de Conclusão: {todosExer[3][2]} segundos -

    + + Concluido +
    +
    +
    + +
    +
    +
    +
  • + {todosExer[3][3]} 50 XP +
    +
    +

    + Exercicio 4 +

    +
    +
    +
  • + {todosExer[3][2]} s
    -
    - +
    +
  • + {todosExer[3][1]}
    + + Concluido
    - - -
    - + + + +
    ); } From 3097077154144b633ef29dddd54cea4a06aea87a Mon Sep 17 00:00:00 2001 From: ArthurVenturi Date: Thu, 12 Jun 2025 22:26:02 -0300 Subject: [PATCH 04/10] =?UTF-8?q?Ajsute=20nos=20personagens=20dos=20exerci?= =?UTF-8?q?cios=20e=20finaliza=C3=A7=C3=A3o=20do=20design=20da=20p=C3=A1gi?= =?UTF-8?q?na=20de=20performance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- index.html | 16 +- public/assets/css/exercicio1.css | 33 +- public/assets/css/exercicio2.css | 36 +- public/assets/css/exercicio3.css | 22 +- public/assets/css/exercicio4.css | 21 - public/assets/css/header.css | 10 +- public/assets/css/performance.css | 491 +++++++++++++----- public/assets/js/exercicio4.js | 2 +- public/img/istockphoto-1393685037-612x612.jpg | Bin 33757 -> 0 bytes public/img/logo.png | Bin 8008 -> 86743 bytes src/components/App.jsx | 1 + src/components/BannerPrincipal.jsx | 2 +- src/components/ErrorBoundaries.jsx | 4 +- src/components/Header.jsx | 9 +- src/components/Servicos.jsx | 8 +- src/pages/Cadastro.jsx | 2 +- src/pages/Exercicio4.jsx | 2 +- src/pages/Home.jsx | 2 +- src/pages/Login.jsx | 4 +- src/routes/login.lazy.jsx | 2 +- src/routes/performance.lazy.jsx | 233 ++++++--- src/routes/statistics.lazy.jsx | 39 +- 23 files changed, 628 insertions(+), 315 deletions(-) delete mode 100644 public/img/istockphoto-1393685037-612x612.jpg diff --git a/.gitignore b/.gitignore index c9a988c..ef2b167 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .env node_modules /dist -.vscode \ No newline at end of file +.vscode + +package-lock.json \ No newline at end of file diff --git a/index.html b/index.html index 1342602..8721bb2 100644 --- a/index.html +++ b/index.html @@ -10,21 +10,19 @@ - - - - + +
    Não Renderizado
    - - + + - - - + + + \ No newline at end of file diff --git a/public/assets/css/exercicio1.css b/public/assets/css/exercicio1.css index d7b1cc6..a630b02 100644 --- a/public/assets/css/exercicio1.css +++ b/public/assets/css/exercicio1.css @@ -126,13 +126,26 @@ p { } .personagem { + position: fixed; + left: 40px; + bottom: 0; + z-index: 10; display: flex; - justify-content: center; - align-items: start; - position: relative; + align-items: flex-end; + box-sizing: border-box; + gap: 30px; + justify-content: flex-start; + align-items: center; padding-top: 110px; - padding-right: 60%; - z-index: 2; + padding-right: 55%; + max-width: 95vw; +} + +.personagem img { + height: auto; + width: 100%; + max-height: 300px; + max-width: 300px; } .text-content { @@ -142,8 +155,14 @@ p { z-index: inherit; border-radius: 1rem; padding: 10px; - margin-top: 20px; - margin-left: 20px; + margin-bottom: 40px; +} + +.text-content p { + font-size: 1.2rem; + color: #4c4c4c; + margin: 0; + padding: 0 10px; } /* Estilos responsivos */ diff --git a/public/assets/css/exercicio2.css b/public/assets/css/exercicio2.css index b2d309d..b7fe5cb 100644 --- a/public/assets/css/exercicio2.css +++ b/public/assets/css/exercicio2.css @@ -95,6 +95,7 @@ button:focus { margin-top: -150px; /* background-color: #ede6ace7; */ border-radius: 1rem; + position: absolute; } #message h1 { @@ -147,23 +148,20 @@ button:focus { height: auto; } -.personagem { - display: flex; - justify-content: center; - align-items: start; - position: relative; - padding-top: 450px; - padding-right: 30%; - z-index: 2; -} - -.text-content { - display: flex; - align-items: center; - background-color: white; - z-index: inherit; - border-radius: 1rem; - padding: 10px; - margin-top: 20px; - margin-left: 20px; +@media (max-width: 768px) { + .personagem { + flex-direction: column; + align-items: flex-start; + left: 10px; + bottom: 10px; + width: auto; + max-width: 95vw; + padding: 0; + } + .text-content { + margin-left: 0; + margin-top: 10px; + width: 100%; + box-sizing: border-box; + } } \ No newline at end of file diff --git a/public/assets/css/exercicio3.css b/public/assets/css/exercicio3.css index 02004c7..36d59de 100644 --- a/public/assets/css/exercicio3.css +++ b/public/assets/css/exercicio3.css @@ -120,6 +120,7 @@ button:focus { .wave, .waveInverted{ color: #D6F6DE; } + .wave { position: fixed; bottom: 0; @@ -161,24 +162,3 @@ button:focus { width: 100%; height: auto; } - -.personagem{ - display: flex; - justify-content: center; - align-items: start; - position: relative; - padding-top: 400px; - padding-right: 60%; - z-index: 2; -} - -.text-content{ - display: flex; - align-items: center; - background-color: white; - z-index: inherit; - border-radius: 1rem; - padding: 10px; - margin-top: 20px; - margin-left: -30px; -} diff --git a/public/assets/css/exercicio4.css b/public/assets/css/exercicio4.css index 05934e3..2107e6c 100644 --- a/public/assets/css/exercicio4.css +++ b/public/assets/css/exercicio4.css @@ -224,25 +224,4 @@ h1 { font-size: 3rem; color: rgba(51, 51, 51, 0.365); text-transform: uppercase; -} - -.personagem{ - display: flex; - justify-content: center; - align-items: start; - position: absolute; - padding-top: 450px; - padding-right: 50%; - padding-left: 50px; - z-index: 2; -} - -.text-content{ - display: flex; - align-items: center; - background-color: white; - z-index: inherit; - border-radius: 1rem; - padding: 10px; - margin-top: 20px; } \ No newline at end of file diff --git a/public/assets/css/header.css b/public/assets/css/header.css index 17a1f2e..86e8843 100644 --- a/public/assets/css/header.css +++ b/public/assets/css/header.css @@ -1,6 +1,6 @@ .header-container { display: flex; - justify-content: flex-end; + justify-content: space-between; align-items: center; padding: 14px 32px; background-color: #d6f6de; @@ -19,11 +19,11 @@ } .logo { - width: 48px; - height: 48px; - background: url('/assets/img/logo.png') no-repeat center center; + width: 64px; + height: 64px; + background-repeat: no-repeat; + background-position: center; background-size: contain; - margin-right: 24px; } .flex-container { diff --git a/public/assets/css/performance.css b/public/assets/css/performance.css index bcd642a..81b5e95 100644 --- a/public/assets/css/performance.css +++ b/public/assets/css/performance.css @@ -1,188 +1,425 @@ -.layout { - display: flex; - flex-direction: column; - padding: 1rem; - place-content: center; - box-sizing: border-box; - width: 100vw; - min-height: 100vh; - max-width: 100vw; +/* Layout principal da página de performance */ +.performance-layout { + display: grid; + grid-template-columns: 75% 1fr; + gap: 10px; + width: 100vw; + box-sizing: border-box; +} + +.content-performance{ + padding: 1rem; } -.summary-section h1 { - font-size: 2.5rem; - color: #DBC4F8; - margin-bottom: 20px; +.content-performance.right { + margin: 1rem; + padding: 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border-radius: 1rem; } +/* Seção dos pilares */ .pilares-section { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 10px; - width: 100%; - justify-content: stretch; - align-items: stretch; + display: grid; + grid-template-columns: repeat(4, 1fr); + gap: 20px; + width: 100%; + justify-content: stretch; + align-items: stretch; + box-sizing: border-box; } -.card-pilars{ - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; - background-color: #FDF3D6; - border-radius: 1rem; - padding: 1rem; - text-align: center; - min-width: fit-content; - height: 280px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - position: relative; +/* Card de cada pilar */ +.card-pilars { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + background-color: #f9f9f9; + border-radius: 1rem; + padding: 1rem; + border-left: 8px solid; + text-align: center; + min-width: fit-content; + height: 280px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + position: relative; + box-sizing: border-box; } +/* Informações do pilar */ .card-info { - display: flex; - flex-direction: column; - justify-content: start; - padding: 10px; - gap: 30px; -} + display: flex; + flex-direction: column; + justify-content: start; + padding: 10px; + gap: 30px; +} .card-info img { - display: block; - size: 1rem; - height: 150px; - width: fit-content; - background-size: cover; + display: block; + size: 1rem; + height: 150px; + width: fit-content; + background-size: cover; } .card-info h2 { - font-size: 1.5rem; - color: #333; + font-size: 1.5rem; + color: #333; } +/* Gráfico circular do pilar */ .card-graphic { - position: absolute; - width: 140px; - height: 160px; - display: flex; - justify-content: center; - align-items: center; + position: absolute; + top: 1rem; + right: 1rem; + width: 140px; + height: 140px; + display: flex; + justify-content: center; + align-items: center; + pointer-events: none; } .card-graphic svg { - position: absolute; - width: 140px; - height: 140px; - transform: rotate(-90deg); + position: relative; + width: 100%; + height: 100%; + transform: rotate(-90deg); + display: block; } .card-graphic .background-circle { - fill: none; - stroke: #e0e0e0; - stroke-width: 10; + fill: none; + stroke: #e0e0e0; + stroke-width: 10; } .card-graphic .progress-circle { - fill: none; - stroke: #DBC4F8; - stroke-width: 10; - stroke-linecap: round; - stroke-dasharray: 0, 440; - transition: stroke-dasharray 0.3s ease; + fill: none; + stroke: #dbc4f8; + stroke-width: 10; + stroke-linecap: round; + stroke-dasharray: 0, 440; + transition: stroke-dasharray 0.3s ease; } .card-graphic .percentage { - position: absolute; - font-size: 1.5rem; - font-weight: bold; - color: #333; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-size: 1.5rem; + font-weight: bold; + color: #333; + pointer-events: none; } -.info{ - display: grid; - grid-template-columns: 1.5fr 1fr; - gap: 20px; - margin-top: 2rem; -} - -/* Inicio da stilização da Tabela de Exercicios*/ - +/* Seção da tabela de exercícios */ .exercises-section { - display: flex; - flex-direction: column; - padding: 1rem; - background-color: #f9f9f9; - border-radius: 1rem; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + width: 100%; + height: auto; + padding: 1rem; + margin-top: 4rem; + background-color: #f9f9f9; + border-radius: 1rem; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); } .exercises-section h2 { - font-size: 2rem; - color: #333; - text-align: center; - margin-bottom: 1rem; + font-size: 2rem; + color: #333; + text-align: center; + margin-bottom: 1rem; } +/* Tabela de exercícios */ .exercises-table { - width: 100%; - border-collapse: collapse; - text-align: center; - font-size: 1rem; + width: 100%; + border-collapse: collapse; + text-align: center; + font-size: 1rem; } .exercises-table th, .exercises-table td { - padding: 10px; - border: 1px solid #ddd; + padding: 10px; + border: 1px solid #ddd; } .exercises-table th { - background-color: #FDF3D6; - color: #333; - font-weight: bold; + background-color: #fdf3d6; + color: #333; + font-weight: bold; } .exercises-table td .status-button { - display: inline-block; - padding: 5px 10px; - border-radius: 1rem; - font-size: 0.9rem; - font-weight: bold; - color: #fff; - text-align: center; + display: inline-block; + padding: 5px 10px; + border-radius: 1rem; + font-size: 0.9rem; + font-weight: bold; + color: #fff; + text-align: center; } .exercises-table td .status-button.completed { - background-color: #4caf50; + background-color: #4caf50; } .exercises-table td .status-button.incomplete { - background-color: #f44336; + background-color: #f44336; +} + +/* ================= Ranking Table ================= */ +.ranking-table { + background: #f9f9f9; + padding: 1rem; + display: flex; + flex-direction: column; + align-items: center; + max-height: 350px; +} + +.ranking-table-scroll { + max-height: 100%; + overflow-y: auto; + width: 100%; +} + +.ranking-header { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + gap: 30px; + position: relative; + margin-bottom: 1rem; +} + +.ranking-header h2 { + font-size: 2rem; + color: #333; + flex: 1; + text-align: center; +} + +.ranking-table-inner { + width: 100%; + border-collapse: collapse; + font-size: 1rem; + background: #fff; + border-radius: 0.5rem; + overflow: hidden; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04); +} + +.ranking-table-inner th, +.ranking-table-inner td { + padding: 10px 16px; + border-bottom: 1px solid #eee; + text-align: center; +} + +.ranking-table-inner th { + background: #e9e3f7; + color: #333; + font-weight: bold; +} + +.ranking-table-inner tr:last-child td { + border-bottom: none; +} + +.ranking-table-inner tr:nth-child(4) td { + background: #f8dd56; + color: #333; + font-weight: bold; +} + +/* ===== Botão e Modal de Pontuação ===== */ +.pontuation-info-btn { + position: absolute; + right: 10px; + width: 48px; + height: 48px; + min-width: 48px; + min-height: 48px; + max-width: 48px; + max-height: 48px; + display: flex; + align-items: center; + justify-content: center; + background: #e9e3f7; + color: #333; + border: none; + border-radius: 50%; + font-size: 1rem; + font-weight: bold; + cursor: pointer; + transition: background 0.2s; + margin-left: auto; } +.pontuation-info-btn i { + font-size: 1.5rem; +} + +.pontuation-info-btn:hover { + background: #d5c2e0; +} + +.pontuation-modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.25); + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +.pontuation-modal { + background: #fff; + border-radius: 1rem; + padding: 2rem 1.5rem 1.5rem 1.5rem; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.18); + max-width: 350px; + width: 90vw; + position: relative; + text-align: left; + animation: pontuationModalIn 0.2s; +} + +@keyframes pontuationModalIn { + from { + transform: scale(0.95); + opacity: 0; + } + to { + transform: scale(1); + opacity: 1; + } +} + +.pontuation-modal h3 { + margin-top: 0; + font-size: 1.3rem; + color: #6c4fa1; +} + +.pontuation-modal ul { + margin: 1rem 0 0 1.2rem; + padding: 0; + color: #444; + font-size: 1rem; +} + +.pontuation-modal-close { + position: absolute; + top: 0.7rem; + right: 1rem; + background: none; + border: none; + font-size: 1.5rem; + color: #888; + cursor: pointer; + transition: color 0.2s; +} +.pontuation-modal-close:hover { + color: #333; +} + +/* ================= Estilo do Perfil ================= */ +.performance-profile { + display: flex; + flex-direction: column; + align-items: center; + border-radius: 1rem; + padding: 2.8rem 0; + background-color: #f9f9f9; + position: relative; + box-sizing: border-box; + border-top: 8px solid; +} + +.profile-circle { + width: 160px; + height: 160px; + border-radius: 50%; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border: 6px solid; +} + +.profile-rank { + font-size: 2.5rem; + font-weight: bold; + color: #6c4fa1; +} + +.profile-stars { + display: flex; + gap: 12px; + margin-top: 8px; +} + +.profile-note { + margin-top: 12px; + font-size: 1.2rem; + color: #333; + text-align: center; + font-weight: bold; +} + +/* ================= Responsividade ================= */ @media (max-width: 900px) { - .pilares-section { - grid-template-columns: 1fr 1fr; - } - .info { - grid-template-columns: 1fr; - } + .performance-layout { + grid-template-columns: 1fr; + } + .pilares-section { + grid-template-columns: 1fr 1fr; + } + .info { + grid-template-columns: 1fr; + } + .ranking-table { + margin-top: 0; + margin-bottom: 1rem; + } } @media (max-width: 600px) { - .layout { - padding: 1rem; - gap: 1rem; - } - .pilares-section { - grid-template-columns: 1fr; - gap: 10px; - } - .card-pilars { - height: auto; - grid-template-columns: 1fr; - } - .info { - grid-template-columns: 1fr; - gap: 10px; - } -} + .layout { + padding: 1rem; + gap: 1rem; + } + .performance-layout { + grid-template-columns: 1fr; + } + .pilares-section { + grid-template-columns: 1fr; + gap: 10px; + } + .card-pilars { + height: auto; + grid-template-columns: 1fr; + } + .info { + grid-template-columns: 1fr; + gap: 10px; + } + .ranking-table-inner th, + .ranking-table-inner td { + padding: 8px 4px; + font-size: 0.95rem; + } +} \ No newline at end of file diff --git a/public/assets/js/exercicio4.js b/public/assets/js/exercicio4.js index daf487c..b02c9c9 100644 --- a/public/assets/js/exercicio4.js +++ b/public/assets/js/exercicio4.js @@ -43,7 +43,7 @@ export function addDraggableDivs(draggableDivs, cells) { let shufflePosition = shufflePositions(); draggableDivs.forEach((div, i) => { - div.style.backgroundImage = 'url("../public/img/grupoMascotes.png")'; + div.style.backgroundImage = 'url("/img/grupoMascotes.png")'; cells.append(div); div.style.backgroundPosition = `-${Positions[i][1]}vw -${Positions[i][0]}vw`; div.style.left = `${shufflePosition[i][0]}vw`; diff --git a/public/img/istockphoto-1393685037-612x612.jpg b/public/img/istockphoto-1393685037-612x612.jpg deleted file mode 100644 index f085bb0c0f8ed278fc1f5026f9ef3fc8ad57a686..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33757 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 LT|^!cey{u=BPKur diff --git a/public/img/logo.png b/public/img/logo.png index f8ce912c84f9f69ce5c52aeba57b9d454f372256..bfccbfb8d709d98284950bc11ee6c1ff0a54f1a8 100644 GIT binary patch literal 86743 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 literal 8008 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 ZYf_Q<`X832>?!}Tbu|q&D%Bma{{up&*Ma~5 diff --git a/src/components/App.jsx b/src/components/App.jsx index 39212ed..bab77cc 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -4,6 +4,7 @@ import { routeTree } from "../routeTree.gen"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRouter, RouterProvider } from "@tanstack/react-router"; import { UserProvider } from "../contexts/UserContext"; +import "primeicons/primeicons.css"; const router = createRouter({ routeTree }) const queryClient = new QueryClient(); diff --git a/src/components/BannerPrincipal.jsx b/src/components/BannerPrincipal.jsx index dd5707b..e27521b 100644 --- a/src/components/BannerPrincipal.jsx +++ b/src/components/BannerPrincipal.jsx @@ -25,7 +25,7 @@ const BannerPrincipal = () => {
    - Ilustração + Ilustração
    diff --git a/src/components/ErrorBoundaries.jsx b/src/components/ErrorBoundaries.jsx index 428a05d..1a3cf64 100644 --- a/src/components/ErrorBoundaries.jsx +++ b/src/components/ErrorBoundaries.jsx @@ -37,7 +37,7 @@ class ErrorBoundary extends React.Component {

    WHOPPS!

    -

    Alguma coisa deu errado. Isso é constrangedor

    +

    Alguma coisa deu errado. Que Vergonha

    diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 07e6900..62e5067 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -1,7 +1,7 @@ import { Link } from "@tanstack/react-router"; import "../../public/assets/css/header.css"; import { useUser } from "../contexts/UserContext"; -// import "../../public/assets/css/index.css"; +import "../../public/assets/css/index.css"; const Header = () => { const { user } = useUser(); @@ -11,6 +11,11 @@ const Header = () => { data-wow-duration="0.75s" data-wow-delay="0s" > +
    - lilu + lilu
    @@ -80,7 +80,7 @@ const Servicos = () => {
    - cadu + cadu
    @@ -104,7 +104,7 @@ const Servicos = () => {
    - ana + ana
    @@ -128,7 +128,7 @@ const Servicos = () => {
    - soso + soso
    diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index 94f42ad..2d499c7 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -27,7 +27,7 @@ const Cadastro = () => { return ( <> - wave + wave
    grupoMascotes diff --git a/src/pages/Exercicio4.jsx b/src/pages/Exercicio4.jsx index 24081ac..223324f 100644 --- a/src/pages/Exercicio4.jsx +++ b/src/pages/Exercicio4.jsx @@ -35,7 +35,7 @@ const Exercicio4 = () => {
    toggleVisablity("personagem")} style={{ visibility: "visible" }}> - Soso + Soso

    Ajude-me a montar o quebra-cabeça com a foto da nossa turma diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 8fd2fa6..7540732 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -12,7 +12,7 @@ const Home = () => {

    Aprenda sobre tecnologia de forma divertida e interativa!

    - Ilustração + Ilustração
    diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 312d04e..da7c964 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -25,10 +25,10 @@ const Login = () => { return ( <> - wave + wave
    - grupoMascotes + grupoMascotes
    {mutation.isSuccess ? ( diff --git a/src/routes/login.lazy.jsx b/src/routes/login.lazy.jsx index 95ec8ac..4d036ae 100644 --- a/src/routes/login.lazy.jsx +++ b/src/routes/login.lazy.jsx @@ -1,5 +1,5 @@ import { createLazyFileRoute } from '@tanstack/react-router' -import Login from '../pages/login' +import Login from '../pages/Login' export const Route = createLazyFileRoute('/login')({ component: LoginComponent, diff --git a/src/routes/performance.lazy.jsx b/src/routes/performance.lazy.jsx index b6de85f..f7ce72f 100644 --- a/src/routes/performance.lazy.jsx +++ b/src/routes/performance.lazy.jsx @@ -1,6 +1,8 @@ import { createLazyFileRoute } from "@tanstack/react-router"; +import { useState } from "react"; import "../../public/assets/css/performance.css"; import Header from "../components/Header"; +import { Star } from "./statistics.lazy"; export const Route = createLazyFileRoute("/performance")({ component: PerformanceComponent, @@ -10,8 +12,7 @@ function PerformanceComponent() { const keys = Object.keys(localStorage).filter((key) => key.startsWith("Exer") ); - - // Add static fallback values for testing + const defaultTodosExer = [ [0, 2, "00:01:30"], [0, 1, "00:02:10"], @@ -25,15 +26,16 @@ function PerformanceComponent() { : defaultTodosExer; const pillars = [ - { name: "Decomposição", img: "/img/ana.png", lessons: 8, total: 10 }, + { name: "Decomposição", img: "/img/ana.png", lessons: 8, total: 10, color: "#D5C2E0", }, { name: "Reconhecimento de Padrões", img: "/img/lilu.png", lessons: 6, total: 10, + color: "#F8DD56", }, - { name: "Abstração", img: "/img/soso.png", lessons: 7, total: 10 }, - { name: "Algoritmos", img: "/img/cadu.png", lessons: 9, total: 10 }, + { name: "Abstração", img: "/img/soso.png", lessons: 9, total: 10, color: "#78BD77" }, + { name: "Algoritmos", img: "/img/cadu.png", lessons: 9, total: 10, color: "#C2DAD6" }, ]; const exercises = [ @@ -63,82 +65,173 @@ function PerformanceComponent() { }, ]; + const [showModal, setShowModal] = useState(false); + + const ranking = [ + { pos: 1, user: "Ana", score: 950 }, + { pos: 2, user: "João", score: 900 }, + { pos: 3, user: "Maria", score: 850 }, + { pos: 4, user: "Você", score: 800 }, + { pos: 5, user: "Lucas", score: 750 }, + { pos: 6, user: "Pedro", score: 700 }, + { pos: 7, user: "Clara", score: 650 }, + { pos: 8, user: "Rafaela", score: 600 }, + { pos: 9, user: "Bruno", score: 550 }, + { pos: 10, user: "Gabriel", score: 500 }, + ]; + const userRow = ranking.find(r => r.user === "Você"); + const userPos = userRow ? userRow.pos : 0; + + let filledStars = 0; + if (userPos === 1) filledStars = 3; + else if (userPos === 2 || userPos === 3) filledStars = 2; + else if (userPos === 4 || userPos === 5) filledStars = 1; + return ( <>
    -
    -
    -

    Resumo

    -
    -
    - {pillars.map((pillar, index) => ( -
    -
    - {pillar.name} -

    {pillar.name}

    -
    -
    - - - - -
    - {`${pillar.lessons}/${pillar.total}`} +
    +
    +
    + {pillars.map((pillar, index) => ( +
    +
    + {pillar.name} +

    {pillar.name}

    +
    +
    + + + + +
    + {`${((pillar.lessons / pillar.total) * 100)}%`} +
    + ))} +
    +
    +
    +

    Exercícios

    + + + + + + + + + + + {exercises.map((exercise, index) => ( + + + + + + + ))} + +
    NomeTempoTentativasStatus
    {exercise.name}{exercise.time}{exercise.tries} + + {exercise.completed ? "Concluído" : "Não Concluído"} + +
    - ))} +
    -
    -
    -

    Exercícios

    - - - - - - - - - - - {exercises.map((exercise, index) => ( - +
    +
    +
    {userPos ? `${userPos}º` : "--"}
    +
    +
    + + + +
    +
    + {userPos && userPos <= 5 ? "Você está no Top 5!" : "Continue para subir no ranking!"} +
    +
    +
    +
    +

    Ranking

    + + {showModal && ( +
    setShowModal(false)}> +
    e.stopPropagation()} > -
    - - - + +

    Sistema de Pontuação

    +
      +
    • +1000 pontos: Concluir todos os exercícios sem erros
    • +
    • +900 pontos: Concluir com até 1 erro
    • +
    • +800 pontos: Concluir com até 2 erros
    • +
    • +1 ponto: Para cada segundo a menos no tempo total
    • +
    • Estrelas: Top 1 (3⭐), Top 2-3 (2⭐), Top 4-5 (1⭐)
    • +
    + + + )} + +
    +
    NomeTempoTentativasStatus
    {exercise.name}{exercise.time}{exercise.tries} - - {exercise.completed ? "Concluído" : "Não Concluído"} - -
    + + + + + - ))} - -
    PosiçãoUsuárioPontuação
    + + + {ranking.map((row) => ( + + {row.pos} + {row.user} + {row.score} + + ))} + + +
    ); -} +} \ No newline at end of file diff --git a/src/routes/statistics.lazy.jsx b/src/routes/statistics.lazy.jsx index dd7c4ca..6a5dd89 100644 --- a/src/routes/statistics.lazy.jsx +++ b/src/routes/statistics.lazy.jsx @@ -1,6 +1,5 @@ import { createLazyFileRoute, Link } from "@tanstack/react-router"; import "../../public/assets/css/statistics.css"; -import "primeicons/primeicons.css"; export const Route = createLazyFileRoute("/statistics")({ component: statisticsComponent, @@ -13,24 +12,7 @@ function statisticsComponent() { const todosExer = keys.map((key) => JSON.parse(localStorage.getItem(key))); console.log(todosExer) - /* - gold:#C9B037 - silver:#B4B4B4 - bronze:#CD7F32 - */ - const Star = () => ( - - - - ); return (
    @@ -45,7 +27,7 @@ function statisticsComponent() { style={{ borderRight: "5px solid #D5C2E0" }} >
    - +
    @@ -166,3 +148,22 @@ function statisticsComponent() {
    ); } + + /* + gold:#C9B037 + silver:#B4B4B4 + bronze:#CD7F32 + */ + +export const Star = ({ width, height, color }) => ( + + + +); \ No newline at end of file From 228aa25de546a55a5d4112ddc48dbbd6360d43f9 Mon Sep 17 00:00:00 2001 From: ArthurVenturi Date: Mon, 23 Jun 2025 21:52:24 -0300 Subject: [PATCH 05/10] =?UTF-8?q?Implementa=C3=A7=C3=A3o=20do=20cadastro?= =?UTF-8?q?=20e=20login=20com=20o=20backend,=20nivel=20de=20acesso=20de=20?= =?UTF-8?q?usu=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +- package-lock.json | 139 ++++++++++++++++++++++--- package.json | 1 + public/assets/css/cadastro.css | 3 +- public/assets/css/exercicio1.css | 1 - public/assets/css/header.css | 39 +++++-- public/assets/css/home.css | 14 --- public/assets/css/index.css | 41 +++++--- public/assets/css/login.css | 1 + public/assets/css/performance.css | 7 +- public/assets/js/custom.js | 166 +++++++++++++++--------------- public/assets/js/exercicio2.js | 2 +- src/api/User.jsx | 71 ++++++++----- src/components/Conteudos.jsx | 8 +- src/components/Header.jsx | 21 +++- src/components/Loading.jsx | 38 ++++--- src/components/successPopUp.jsx | 15 --- src/contexts/UserContext.jsx | 30 +++++- src/pages/Cadastro.jsx | 34 ++++-- src/pages/Exercicio1.jsx | 6 +- src/pages/Exercicio2.jsx | 12 ++- src/pages/Exercicio3.jsx | 50 ++++++--- src/pages/Exercicio4.jsx | 33 ++++-- src/pages/Login.jsx | 23 +++-- src/routes/exercise/$id.lazy.jsx | 2 +- src/routes/performance.lazy.jsx | 10 +- 26 files changed, 500 insertions(+), 271 deletions(-) delete mode 100644 src/components/successPopUp.jsx diff --git a/.gitignore b/.gitignore index ef2b167..5850423 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,6 @@ node_modules /dist .vscode -package-lock.json \ No newline at end of file +package-lock.json + +*log \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 0bf3a7e..9cf3971 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "jquery": "^3.7.1", "jquery-touchswipe": "^1.6.19", "primeicons": "^7.0.0", + "prisma": "^6.10.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -21,6 +22,7 @@ "@tanstack/eslint-plugin-query": "^5.62.16", "@tanstack/router-plugin": "^1.97.1", "@testing-library/react": "^16.2.0", + "@types/react": "^19.1.8", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.18.0", "eslint-plugin-react": "^7.37.4", @@ -841,9 +843,9 @@ } }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -902,9 +904,9 @@ } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1129,6 +1131,60 @@ "node": ">= 8" } }, + "node_modules/@prisma/config": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.10.1.tgz", + "integrity": "sha512-kz4/bnqrOrzWo8KzYguN0cden4CzLJJ+2VSpKtF8utHS3l1JS0Lhv6BLwpOX6X9yNreTbZQZwewb+/BMPDCIYQ==", + "license": "Apache-2.0", + "dependencies": { + "jiti": "2.4.2" + } + }, + "node_modules/@prisma/debug": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.10.1.tgz", + "integrity": "sha512-k2YT53cWxv9OLjW4zSYTZ6Z7j0gPfCzcr2Mj99qsuvlxr8WAKSZ2NcSR0zLf/mP4oxnYG842IMj3utTgcd7CaA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.10.1.tgz", + "integrity": "sha512-Q07P5rS2iPwk2IQr/rUQJ42tHjpPyFcbiH7PXZlV81Ryr9NYIgdxcUrwgVOWVm5T7ap02C0dNd1dpnNcSWig8A==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.10.1", + "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", + "@prisma/fetch-engine": "6.10.1", + "@prisma/get-platform": "6.10.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c.tgz", + "integrity": "sha512-ZJFTsEqapiTYVzXya6TUKYDFnSWCNegfUiG5ik9fleQva5Sk3DNyyUi7X1+0ZxWFHwHDr6BZV5Vm+iwP+LlciA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.10.1.tgz", + "integrity": "sha512-clmbG/Jgmrc/n6Y77QcBmAUlq9LrwI9Dbgy4pq5jeEARBpRCWJDJ7PWW1P8p0LfFU0i5fsyO7FqRzRB8mkdS4g==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.10.1", + "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", + "@prisma/get-platform": "6.10.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.10.1.tgz", + "integrity": "sha512-4CY5ndKylcsce9Mv+VWp5obbR2/86SHOLVV053pwIkhVtT9C9A83yqiqI/5kJM9T1v1u1qco/bYjDKycmei9HA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.10.1" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", @@ -1756,6 +1812,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/react": { + "version": "19.1.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", + "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.26.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz", @@ -2208,9 +2274,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2435,6 +2501,13 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -2946,9 +3019,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3000,9 +3073,9 @@ } }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -4029,6 +4102,15 @@ "node": ">= 0.4" } }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/jquery": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", @@ -4686,6 +4768,31 @@ "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", "license": "MIT" }, + "node_modules/prisma": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.10.1.tgz", + "integrity": "sha512-khhlC/G49E4+uyA3T3H5PRBut486HD2bDqE2+rvkU0pwk9IAqGFacLFUyIx9Uw+W2eCtf6XGwsp+/strUwMNPw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.10.1", + "@prisma/engines": "6.10.1" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5515,7 +5622,7 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "peer": true, "bin": { diff --git a/package.json b/package.json index d841c43..c20ac5b 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@tanstack/eslint-plugin-query": "^5.62.16", "@tanstack/router-plugin": "^1.97.1", "@testing-library/react": "^16.2.0", + "@types/react": "^19.1.8", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.18.0", "eslint-plugin-react": "^7.37.4", diff --git a/public/assets/css/cadastro.css b/public/assets/css/cadastro.css index 1299477..c095a13 100644 --- a/public/assets/css/cadastro.css +++ b/public/assets/css/cadastro.css @@ -213,6 +213,7 @@ a:hover { justify-content: center; } } + .border-first-button a { display: inline-block !important; padding: 10px 20px !important; @@ -264,4 +265,4 @@ a:hover { .login-message a:hover { color: #c197f5; /* Efeito hover para o link */ -} +} \ No newline at end of file diff --git a/public/assets/css/exercicio1.css b/public/assets/css/exercicio1.css index a630b02..9a52090 100644 --- a/public/assets/css/exercicio1.css +++ b/public/assets/css/exercicio1.css @@ -144,7 +144,6 @@ p { .personagem img { height: auto; width: 100%; - max-height: 300px; max-width: 300px; } diff --git a/public/assets/css/header.css b/public/assets/css/header.css index 86e8843..5295c33 100644 --- a/public/assets/css/header.css +++ b/public/assets/css/header.css @@ -3,8 +3,8 @@ justify-content: space-between; align-items: center; padding: 14px 32px; - background-color: #d6f6de; - box-shadow: 0 4px 12px rgba(0,0,0,0.04); + background-color: #000000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04); box-sizing: border-box; } @@ -39,6 +39,32 @@ align-items: center; } +.scroll-to-section a:hover { + color: #c197f5; + /* Laranja Claro */ +} + +.border-first-button a { + display: inline-block !important; + padding: 10px 20px !important; + background-color: #dbc4f8; + /* Rosa Claro */ + color: #fff; + /* Branco */ + border: 1px solid #dbc4f8; + /* Rosa Claro */ + border-radius: 23px; + font-weight: 500 !important; + letter-spacing: 0.3px !important; + transition: all 0.5s; +} + +.border-first-button a:hover { + background-color: #c197f5; /* Laranja Claro */ + color: #fff !important; + border: 1px solid #c197f5; /* Rosa Claro */ +} + .login-button { color: #fff; border-radius: 6px; @@ -47,12 +73,5 @@ } .login-button a:hover { - color: black !important; -} - -.border-first-button { - border-radius: 6px; - padding: 6px 18px; - margin-left: 8px; - transition: background 0.2s, color 0.2s; + color: #c197f5 !important; } \ No newline at end of file diff --git a/public/assets/css/home.css b/public/assets/css/home.css index 89ebf84..f90b400 100644 --- a/public/assets/css/home.css +++ b/public/assets/css/home.css @@ -50,7 +50,6 @@ header { } .main-banner { - background-color: #FDF3D6; padding: 60px 20px; display: flex; align-items: center; @@ -139,7 +138,6 @@ header { background-color: #D3EDC5; padding: 40px 20px; text-align: center; - } .about-section h2 { @@ -154,7 +152,6 @@ header { } .content-section { - background-color: #FDF3D6; padding: 60px 20px; text-align: center; } @@ -207,17 +204,6 @@ footer { footer p { color: #fff; } -.content-section { - background-color: #FDF3D6; - padding: 60px 20px; - text-align: center; -} - -.content-section h2 { - font-size: 2.5rem; - color: #DBC4F8; - margin-bottom: 40px; -} .content-grid { display: grid; diff --git a/public/assets/css/index.css b/public/assets/css/index.css index ae3420e..bfd53d6 100644 --- a/public/assets/css/index.css +++ b/public/assets/css/index.css @@ -154,23 +154,6 @@ body { background-color: #726ae3; } -.border-first-button a { - display: inline-block !important; - padding: 10px 20px !important; - background-color: #dbc4f8; /* Rosa Claro */ - color: #fff; /* Branco */ - border: 1px solid #dbc4f8; /* Rosa Claro */ - border-radius: 23px; - font-weight: 500 !important; - letter-spacing: 0.3px !important; - transition: all 0.5s; -} - -.border-first-button a:hover { - background-color: #c197f5; /* Laranja Claro */ - color: #fff !important; -} - .intro-banner { text-align: center; padding-top: 200px; @@ -1879,6 +1862,30 @@ footer p a { transition: all 0.5s; } +/*return button*/ +.return-button { + position: absolute; + top: 24px; + left: 32px; + z-index: 1000; + background: #dbc4f8; + color: #fff; + padding: 8px 18px; + border-radius: 23px; + font-weight: 500; + text-decoration: none; + font-size: 1rem; + transition: background 0.3s, color 0.3s; + box-shadow: 0 2px 8px rgba(0,0,0,0.08); +} + +.return-button:hover { + background: #c197f5; + color: #fff; +} + +/* Responsive Styles */ + @media (max-width: 1200px) { .header-area .main-nav .logo h4 { font-size: 24px; diff --git a/public/assets/css/login.css b/public/assets/css/login.css index d92acc5..51fd977 100644 --- a/public/assets/css/login.css +++ b/public/assets/css/login.css @@ -216,6 +216,7 @@ a:hover{ justify-content: center; } } + .border-first-button a { display: inline-block !important; padding: 10px 20px !important; diff --git a/public/assets/css/performance.css b/public/assets/css/performance.css index 81b5e95..03efdc2 100644 --- a/public/assets/css/performance.css +++ b/public/assets/css/performance.css @@ -311,10 +311,14 @@ margin-top: 0; font-size: 1.3rem; color: #6c4fa1; + margin-left: 1rem; } .pontuation-modal ul { - margin: 1rem 0 0 1.2rem; + display: flex; + flex-direction: column; + gap: 8px; + margin: 1rem; padding: 0; color: #444; font-size: 1rem; @@ -331,6 +335,7 @@ cursor: pointer; transition: color 0.2s; } + .pontuation-modal-close:hover { color: #333; } diff --git a/public/assets/js/custom.js b/public/assets/js/custom.js index 523628a..55bed6d 100644 --- a/public/assets/js/custom.js +++ b/public/assets/js/custom.js @@ -1,9 +1,9 @@ (function ($) { - - "use strict"; - // Header Type = Fixed - $(window).scroll(function() { + "use strict"; + + // Header Type = Fixed + $(window).scroll(function () { var scroll = $(window).scrollTop(); var box = $('.header-text').height(); var header = $('header').height(); @@ -16,30 +16,30 @@ }); - $('.loop').owlCarousel({ - center: true, - items:1, - loop:true, - autoplay: true, - nav: true, - margin:0, - responsive:{ - 1200:{ - items:5 - }, - 992:{ - items:3 - }, - 760:{ - items:2 - } + $('.loop').owlCarousel({ + center: true, + items: 1, + loop: true, + autoplay: true, + nav: true, + margin: 0, + responsive: { + 1200: { + items: 5 + }, + 992: { + items: 3 + }, + 760: { + items: 2 } + } }); - - // Menu Dropdown Toggle - if($('.menu-trigger').length){ - $(".menu-trigger").on('click', function() { + + // Menu Dropdown Toggle + if ($('.menu-trigger').length) { + $(".menu-trigger").on('click', function () { $(this).toggleClass('active'); $('.header-area .nav').slideToggle(200); }); @@ -47,16 +47,16 @@ // Menu elevator animation - $('.scroll-to-section a[href*=\\#]:not([href=\\#])').on('click', function() { - if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) { + $('.scroll-to-section a[href*=\\#]:not([href=\\#])').on('click', function () { + if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { var target = $(this.hash); - target = target.length ? target : $('[name=' + this.hash.slice(1) +']'); + target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); if (target.length) { var width = $(window).width(); - if(width < 991) { + if (width < 991) { $('.menu-trigger').removeClass('active'); - $('.header-area .nav').slideUp(200); - } + $('.header-area .nav').slideUp(200); + } $('html,body').animate({ scrollTop: (target.offset().top) + 1 }, 700); @@ -66,79 +66,79 @@ }); $(document).ready(function () { - $(document).on("scroll", onScroll); - - //smoothscroll - $('.scroll-to-section a[href^="#"]').on('click', function (e) { - e.preventDefault(); - $(document).off("scroll"); - - $('.scroll-to-section a').each(function () { - $(this).removeClass('active'); - }) - $(this).addClass('active'); - - var target = this.hash, - menu = target; - var target = $(this.hash); - $('html, body').stop().animate({ - scrollTop: (target.offset().top) + 1 - }, 500, 'swing', function () { - window.location.hash = target; - $(document).on("scroll", onScroll); - }); + $(document).on("scroll", onScroll); + + //smoothscroll + $('.scroll-to-section a[href^="#"]').on('click', function (e) { + e.preventDefault(); + $(document).off("scroll"); + + $('.scroll-to-section a').each(function () { + $(this).removeClass('active'); + }) + $(this).addClass('active'); + + var target = this.hash, + menu = target; + var target = $(this.hash); + $('html, body').stop().animate({ + scrollTop: (target.offset().top) + 1 + }, 500, 'swing', function () { + window.location.hash = target; + $(document).on("scroll", onScroll); }); + }); }); - function onScroll(event){ - var scrollPos = $(document).scrollTop(); - $('.nav a').each(function () { - var currLink = $(this); - var refElement = $(currLink.attr("href")); - if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) { - $('.nav ul li a').removeClass("active"); - currLink.addClass("active"); - } - else{ - currLink.removeClass("active"); - } - }); + function onScroll(event) { + var scrollPos = $(document).scrollTop(); + $('.nav a').each(function () { + var currLink = $(this); + var refElement = $(currLink.attr("href")); + if (refElement.position().top <= scrollPos && refElement.position().top + refElement.height() > scrollPos) { + $('.nav ul li a').removeClass("active"); + currLink.addClass("active"); + } + else { + currLink.removeClass("active"); + } + }); } // Acc - $(document).on("click", ".naccs .menu div", function() { + $(document).on("click", ".naccs .menu div", function () { var numberIndex = $(this).index(); if (!$(this).is("active")) { - $(".naccs .menu div").removeClass("active"); - $(".naccs ul li").removeClass("active"); + $(".naccs .menu div").removeClass("active"); + $(".naccs ul li").removeClass("active"); - $(this).addClass("active"); - $(".naccs ul").find("li:eq(" + numberIndex + ")").addClass("active"); + $(this).addClass("active"); + $(".naccs ul").find("li:eq(" + numberIndex + ")").addClass("active"); - var listItemHeight = $(".naccs ul") - .find("li:eq(" + numberIndex + ")") - .innerHeight(); - $(".naccs ul").height(listItemHeight + "px"); - } + var listItemHeight = $(".naccs ul") + .find("li:eq(" + numberIndex + ")") + .innerHeight(); + $(".naccs ul").height(listItemHeight + "px"); + } }); - // Page loading animation - $(window).on('load', function() { + // Page loading animation + $(window).on('load', function () { - $('#js-preloader').addClass('loaded'); + $('#js-preloader').addClass('loaded'); + + }); - }); - - // Window Resize Mobile Menu Fix + // Window Resize Mobile Menu Fix function mobileNav() { var width = $(window).width(); - $('.submenu').on('click', function() { - if(width < 767) { + $('.submenu').on('click', function () { + if (width < 767) { $('.submenu ul').removeClass('active'); $(this).find('ul').toggleClass('active'); } diff --git a/public/assets/js/exercicio2.js b/public/assets/js/exercicio2.js index 117d66d..2f9bc8c 100644 --- a/public/assets/js/exercicio2.js +++ b/public/assets/js/exercicio2.js @@ -1,4 +1,4 @@ -import { mazeCanvas, virtCanvas, context, imgData, ctx } from '../../../src/pages/exercicio2'; +import { mazeCanvas, virtCanvas, context, imgData, ctx } from '../../../src/pages/Exercicio2'; import $ from 'jquery'; import 'jquery-touchswipe/jquery.touchSwipe.min.js'; import { toggleVisablity } from '../../../src/utils/utilidades'; diff --git a/src/api/User.jsx b/src/api/User.jsx index 264f2ae..9df3796 100644 --- a/src/api/User.jsx +++ b/src/api/User.jsx @@ -1,34 +1,49 @@ -export async function login(email, password) { - const response = await fetch("http://localhost:3000/login", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ email, password }), - }).catch((e) => {throw new Error("Erro de conexão!", e)}); - - if (!response.ok) { - throw new Error("Login Falhou!"); - } +export async function login(email, password) { + try { + const response = await fetch("http://localhost:3000/login", { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ email, password }), + }).catch((e) => { throw new Error("Erro de conexão!", e) }); - const data = await response.json(); - return data; -}; + if (response.status !== 202) { + throw new Error("Login Falhou!"); + } + + const data = await response.json(); + return data; -export async function cadastro(nome, anoNascimento, email, senha) { - const response = await fetch("http://localhost:3000/register", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ nome, anoNascimento, email, senha }), - }).catch((e) => {throw new Error("Erro de conexão!", e)}); - - if (!response.ok) { - throw new Error("Cadastro Falhou!"); + } catch (e) { + console.error("Erro ao fazer login:", e); + throw e; } +}; + +export async function cadastro(nome, anoNascimento, email, senha) { + try { + const response = await fetch("http://localhost:3000/register", { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ nome, anoNascimento, email, senha }), + }).catch((e) => { throw new Error("Erro de conexão!", e) }); - const data = await response.json(); - return data; + if (response.status !== 201) { + throw new Error("Cadastro Falhou!"); + } + + const data = await response.json(); + + return data; + + } catch (e) { + console.error("Erro ao fazer o cadastro:", e); + throw e; + } }; diff --git a/src/components/Conteudos.jsx b/src/components/Conteudos.jsx index ca6091d..1d0abe7 100644 --- a/src/components/Conteudos.jsx +++ b/src/components/Conteudos.jsx @@ -9,22 +9,22 @@ const Conteudos = () => {

    Pintando o Arco-Íris

    Arraste e organize as cores na ordem correta do arco-íris.

    - Começar Jogo + Começar Jogo

    Robô no Labirinto

    Ajude o robô a sair do labirinto, organizando as instruções corretas.

    - Começar Jogo + Começar Jogo

    Caça ao Tesouro

    Encontre o tesouro seguindo padrões de pistas escondidas.

    - Começar Jogo + Começar Jogo

    Quebra-Cabeça Gigante

    Encontre o tesouro seguindo padrões de pistas escondidas.

    - Começar Jogo + Começar Jogo
    diff --git a/src/components/Header.jsx b/src/components/Header.jsx index 62e5067..fc14fba 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -5,6 +5,7 @@ import "../../public/assets/css/index.css"; const Header = () => { const { user } = useUser(); + return (
    { data-wow-delay="0s" > @@ -21,7 +22,11 @@ const Header = () => {
    • - + Home
      @@ -37,19 +42,25 @@ const Header = () => { {user ? (
    • - Relatório + + Relatório +
    • ) : ( <>
    • - Login + + Login +
    • - Criar conta + + Criar conta +
    • diff --git a/src/components/Loading.jsx b/src/components/Loading.jsx index df5cce3..b95f2d2 100644 --- a/src/components/Loading.jsx +++ b/src/components/Loading.jsx @@ -1,16 +1,24 @@ -const Loading = () => { - return ( -
      -
      - -
      - - - -
      -
      -
      - ); -}; +const Loading = (props) => ( +
      + + +
      +); -export default Loading \ No newline at end of file + +export default Loading; \ No newline at end of file diff --git a/src/components/successPopUp.jsx b/src/components/successPopUp.jsx deleted file mode 100644 index 41eeb27..0000000 --- a/src/components/successPopUp.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import '../../public/assets/css/successPopUp.css'; -import { Link } from '@tanstack/react-router'; - -const SuccessPopUp = ({ message }) => { - return ( -
      -
      -

      {message}

      - Ir para a Home -
      -
      - ); -}; - -export default SuccessPopUp; \ No newline at end of file diff --git a/src/contexts/UserContext.jsx b/src/contexts/UserContext.jsx index 24a72ad..d145452 100644 --- a/src/contexts/UserContext.jsx +++ b/src/contexts/UserContext.jsx @@ -1,4 +1,4 @@ -import { createContext, useContext, useState } from "react" +import { createContext, useContext, useState, useEffect } from "react" //creating a context to store the user data with a default value as null and no function const UserContext = createContext({ @@ -9,18 +9,38 @@ const UserContext = createContext({ }) export const UserProvider = ({ children }) => { - const [user, setUser] = useState(null) + const [user, setUser] = useState(null); + + // Fetch user info from backend on mount (after reload) + useEffect(() => { + async function fetchUser() { + try { + const res = await fetch("http://localhost:3000/sesion", { + credentials: "include", // send cookies + headers: { + "Content-Type": "application/json" + } + }); + if (res.ok) { + const data = await res.json(); + setUser(data.user); + } + } catch (e) { + setUser(null); + } + } + fetchUser(); + }, []); const armazenarLogin = (data) => { if (data !== null) { - setUser(data.user) - console.log(user); + setUser(data); } } const armazenarCadastro = (data) => { if (data !== null) { - setUser(data.user) + setUser(data) console.log(user); } } diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index 2d499c7..8bf0af7 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -1,10 +1,10 @@ -import { Link } from '@tanstack/react-router'; +import { Link, useNavigate } from '@tanstack/react-router'; import '../../public/assets/css/cadastro.css' import { useMutation } from '@tanstack/react-query'; import { cadastro } from '../api/User'; import { useState } from 'react'; import { useUser } from '../contexts/UserContext'; -import SuccessPopUp from '../components/successPopUp'; +import Loading from '../components/Loading'; const Cadastro = () => { const [nome, setNome] = useState(''); @@ -12,30 +12,38 @@ const Cadastro = () => { const [email, setEmail] = useState(''); const [senha, setSenha] = useState(''); const { armazenarCadastro } = useUser() + const navigate = useNavigate(); const mutation = useMutation({ mutationKey: ['cadastro'], mutationFn: async ({ nome, anoNascimento, email, senha }) => { - cadastro(nome, anoNascimento, email, parseInt(senha)) + return cadastro(nome, parseInt(anoNascimento), email, senha) }, onError: (e) => console.log(e), onSuccess: (data) => { - armazenarCadastro(data) + armazenarCadastro(data.user) console.log('Cadastro realizado com sucesso!', data); + armazenarLogin(data.name); + setTimeout(() => { + navigate({ to: "/home", reloadDocument: true }); + }, 800); }, }) return ( <> + + ← Voltar + wave
      grupoMascotes
      - {mutation.isSuccess ? ( - - ) : ( -
      +
      + {mutation.isPending ? ( + + ) : ( mutation.mutate({ nome, anoNascimento, email, senha })}>

      Cadastro

      @@ -53,6 +61,7 @@ const Cadastro = () => { minLength="3" maxLength="50" title="O nome deve conter apenas letras e espaços" + pattern="[a-z][A-Z]" />
      @@ -71,6 +80,7 @@ const Cadastro = () => { onChange={(e) => setAnoNascimento(e.target.value)} required title="Digite uma data válida no formato DD/MM/AAAA" + pattern='[0-9]' />
      @@ -89,6 +99,7 @@ const Cadastro = () => { minLength="5" maxLength="50" title="Digite um e-mail válido" + regex="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" />
    @@ -105,6 +116,7 @@ const Cadastro = () => { minLength="8" maxLength="20" title="A senha deve ter pelo menos 8 caracteres, incluindo letras e números" + regex="^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d@$!%*?&]{8,}$" />
    @@ -130,11 +142,11 @@ const Cadastro = () => {

    - Já tem uma conta?Faça login! + Já tem uma conta?Faça login!

    -
    - )} + )} +
    ); diff --git a/src/pages/Exercicio1.jsx b/src/pages/Exercicio1.jsx index 336abad..b849c4a 100644 --- a/src/pages/Exercicio1.jsx +++ b/src/pages/Exercicio1.jsx @@ -2,6 +2,7 @@ import { verificarResultado, reiniciarExercicio, embaralharCores, drop, dragOver import '../../public/assets/css/exercicio1.css'; import { useEffect, useRef } from 'react'; import { toggleVisablity } from '../utils/utilidades.js' +import { Link } from "@tanstack/react-router"; const Exercicio1 = () => { const resultadoRef = useRef(null); @@ -46,6 +47,9 @@ const Exercicio1 = () => { return ( <> + + ← Continuar em outra hora ? +

    Organize as Cores do Arco-Íris

    @@ -83,7 +87,7 @@ const Exercicio1 = () => {
    - + ) } diff --git a/src/pages/Exercicio2.jsx b/src/pages/Exercicio2.jsx index 4286f4a..71d70fd 100644 --- a/src/pages/Exercicio2.jsx +++ b/src/pages/Exercicio2.jsx @@ -2,12 +2,13 @@ import { loadEvents } from '../../public/assets/js/exercicio2'; import '../../public/assets/css/exercicio2.css'; import { useEffect, useRef } from 'react'; import { toggleVisablity } from '../utils/utilidades.js' -import { Link } from '@tanstack/react-router'; +import { useNavigate, Link } from '@tanstack/react-router'; export var mazeCanvas, virtCanvas, context, imgData, ctx; const Exercicio2 = () => { const elementsCreated = useRef(false); + const navigate = useNavigate(); useEffect(() => { if (elementsCreated.current) return; @@ -25,6 +26,9 @@ const Exercicio2 = () => { return ( <> + + ← Continuar em outra hora ? +

    Navegar o labirinto

    @@ -32,9 +36,7 @@ const Exercicio2 = () => {

    Parabéns!

    Você finalizou a tarefa.

    - - - +
    @@ -56,4 +58,4 @@ const Exercicio2 = () => { ) } -export default Exercicio2; +export default Exercicio2; \ No newline at end of file diff --git a/src/pages/Exercicio3.jsx b/src/pages/Exercicio3.jsx index 2ae3307..98fe9ee 100644 --- a/src/pages/Exercicio3.jsx +++ b/src/pages/Exercicio3.jsx @@ -1,12 +1,13 @@ -import { loadEvents } from '../../public/assets/js/exercicio3'; -import '../../public/assets/css/exercicio3.css'; -import { useEffect, useRef } from 'react'; -import { Link } from '@tanstack/react-router'; +import { loadEvents } from "../../public/assets/js/exercicio3"; +import "../../public/assets/css/exercicio3.css"; +import { useEffect, useRef } from "react"; +import { Link, useNavigate } from "@tanstack/react-router"; export var mazeCanvas, virtCanvas, context, imgData, ctx; const Exercicio3 = () => { const elementsCreated = useRef(false); + const navigate = useNavigate(); useEffect(() => { if (elementsCreated.current) return; @@ -23,6 +24,13 @@ const Exercicio3 = () => { return ( <> + + ← Continuar em outra hora ? +

    Caça ao Tesouro

    @@ -30,32 +38,42 @@ const Exercicio3 = () => {

    Parabéns!

    Você finalizou a tarefa.

    - - - +
    -
    wave wave
    -
    - +
    +
    - -
    +
    Cadu
    -

    Nevege o labirinto com as flechas do teclado e segue minhas instruções para encontrar o tesouro.

    +

    + Nevege o labirinto com as flechas do teclado e segue minhas + instruções para encontrar o tesouro. +

    -
    +
    - ) -} + ); +}; export default Exercicio3; \ No newline at end of file diff --git a/src/pages/Exercicio4.jsx b/src/pages/Exercicio4.jsx index 223324f..511cf26 100644 --- a/src/pages/Exercicio4.jsx +++ b/src/pages/Exercicio4.jsx @@ -1,11 +1,12 @@ -import { toggleVisablity } from '../utils/utilidades.js' +import { toggleVisablity } from "../utils/utilidades.js"; import "../../public/assets/css/exercicio4.css"; import { useEffect, useRef } from "react"; import { addDraggableDivs, createElments, dragDropEvents } from "../../public/assets/js/exercicio4"; -import { Link } from '@tanstack/react-router'; +import { useNavigate, Link } from "@tanstack/react-router"; const Exercicio4 = () => { const elementsCreated = useRef(false); + const navigate = useNavigate(); useEffect(() => { if (elementsCreated.current) return; @@ -21,20 +22,35 @@ const Exercicio4 = () => { createElments(puzzle, puzzleDivs, draggableDivs, cellsAmount); addDraggableDivs(draggableDivs, cells); - dragDropEvents(draggableDivs, puzzleDivs, modal, cellsAmount, attempt, modalBtn); + dragDropEvents( + draggableDivs, + puzzleDivs, + modal, + cellsAmount, + attempt, + modalBtn + ); elementsCreated.current = true; }, []); return ( <> + + ← Continuar em outra hora ? +

    Monte o Quebra-Cabeça

    -
    toggleVisablity("personagem")} style={{ visibility: "visible" }}> +
    toggleVisablity("personagem")} + style={{ visibility: "visible" }} + > Soso

    @@ -49,9 +65,12 @@ const Exercicio4 = () => {

    Parabéns!

    Você finalizou a tarefa.

    - - - +
    diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index da7c964..9e8b87e 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -1,37 +1,44 @@ -import { Link } from "@tanstack/react-router"; +import { Link, useNavigate } from "@tanstack/react-router"; import "../../public/assets/css/login.css"; import { useState } from "react"; import { useMutation } from "@tanstack/react-query"; import { login } from "../api/User"; import { useUser } from "../contexts/UserContext"; -import SuccessPopUp from "../components/successPopUp"; +import Loading from "../components/Loading"; const Login = () => { const [email, setEmail] = useState(""); const [senha, setSenha] = useState(""); const { armazenarLogin } = useUser(); + const navigate = useNavigate(); const mutation = useMutation({ mutationKey: ["login"], mutationFn: async ({ email, senha }) => { - login(email, parseInt(senha)); + return login(email, senha); }, onError: (e) => console.log(e), onSuccess: (data) => { - armazenarLogin(data); + armazenarLogin(data.user); + setTimeout(() => { + navigate({ to: "/home", reloadDocument: true }); + }, 800); console.log("Cadastro realizado com sucesso!", data); }, }); return ( <> + + ← Voltar + wave
    grupoMascotes
    - {mutation.isSuccess ? ( - + {mutation.isPending ? ( + ) : (
    mutation.mutate({ email, senha })}> @@ -79,7 +86,7 @@ const Login = () => {

    - Não tem uma conta? Crie uma! + Não tem uma conta? Crie uma!

    @@ -89,4 +96,4 @@ const Login = () => { ); }; -export default Login; +export default Login; \ No newline at end of file diff --git a/src/routes/exercise/$id.lazy.jsx b/src/routes/exercise/$id.lazy.jsx index e5b7319..7e19132 100644 --- a/src/routes/exercise/$id.lazy.jsx +++ b/src/routes/exercise/$id.lazy.jsx @@ -1,6 +1,6 @@ import { createLazyFileRoute } from '@tanstack/react-router' import Exercicio1 from '../../pages/Exercicio1' -import Exercicio2 from '../../pages/exercicio2' +import Exercicio2 from '../../pages/Exercicio2' import Exercicio3 from '../../pages/Exercicio3' import Exercicio4 from '../../pages/Exercicio4' diff --git a/src/routes/performance.lazy.jsx b/src/routes/performance.lazy.jsx index f7ce72f..b4eae53 100644 --- a/src/routes/performance.lazy.jsx +++ b/src/routes/performance.lazy.jsx @@ -199,11 +199,11 @@ function PerformanceComponent() {

    Sistema de Pontuação

      -
    • +1000 pontos: Concluir todos os exercícios sem erros
    • -
    • +900 pontos: Concluir com até 1 erro
    • -
    • +800 pontos: Concluir com até 2 erros
    • -
    • +1 ponto: Para cada segundo a menos no tempo total
    • -
    • Estrelas: Top 1 (3⭐), Top 2-3 (2⭐), Top 4-5 (1⭐)
    • +
    • +1000 pontos : Concluir todos os exercícios sem erros
    • +
    • +900 pontos : Concluir com até 1 erro
    • +
    • +800 pontos : Concluir com até 2 erros
    • +
    • +1 ponto : Para cada segundo a menos no tempo total
    • +
    • Estrelas : Top 1 (3⭐), Top 2-3 (2⭐), Top 4-5 (1⭐)
    From 58a597fc2c335e6e922d8318c382a3db5d57b266 Mon Sep 17 00:00:00 2001 From: arthurVenturi Date: Thu, 3 Jul 2025 14:46:15 -0300 Subject: [PATCH 06/10] =?UTF-8?q?Ajustes=20na=20P=C3=A1gina=20de=20Perform?= =?UTF-8?q?ance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 85 ++----------------------------- public/assets/css/performance.css | 8 ++- src/api/Statistics.jsx | 32 ++++++++++++ src/pages/Cadastro.jsx | 4 +- src/routes/performance.lazy.jsx | 63 ++++++++++++++--------- src/routes/statistics.lazy.jsx | 8 +-- 6 files changed, 87 insertions(+), 113 deletions(-) create mode 100644 src/api/Statistics.jsx diff --git a/package-lock.json b/package-lock.json index 9cf3971..715a738 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "jquery": "^3.7.1", "jquery-touchswipe": "^1.6.19", "primeicons": "^7.0.0", - "prisma": "^6.10.1", "react": "^19.0.0", "react-dom": "^19.0.0" }, @@ -1131,60 +1130,6 @@ "node": ">= 8" } }, - "node_modules/@prisma/config": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.10.1.tgz", - "integrity": "sha512-kz4/bnqrOrzWo8KzYguN0cden4CzLJJ+2VSpKtF8utHS3l1JS0Lhv6BLwpOX6X9yNreTbZQZwewb+/BMPDCIYQ==", - "license": "Apache-2.0", - "dependencies": { - "jiti": "2.4.2" - } - }, - "node_modules/@prisma/debug": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.10.1.tgz", - "integrity": "sha512-k2YT53cWxv9OLjW4zSYTZ6Z7j0gPfCzcr2Mj99qsuvlxr8WAKSZ2NcSR0zLf/mP4oxnYG842IMj3utTgcd7CaA==", - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.10.1.tgz", - "integrity": "sha512-Q07P5rS2iPwk2IQr/rUQJ42tHjpPyFcbiH7PXZlV81Ryr9NYIgdxcUrwgVOWVm5T7ap02C0dNd1dpnNcSWig8A==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.10.1", - "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", - "@prisma/fetch-engine": "6.10.1", - "@prisma/get-platform": "6.10.1" - } - }, - "node_modules/@prisma/engines-version": { - "version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c.tgz", - "integrity": "sha512-ZJFTsEqapiTYVzXya6TUKYDFnSWCNegfUiG5ik9fleQva5Sk3DNyyUi7X1+0ZxWFHwHDr6BZV5Vm+iwP+LlciA==", - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.10.1.tgz", - "integrity": "sha512-clmbG/Jgmrc/n6Y77QcBmAUlq9LrwI9Dbgy4pq5jeEARBpRCWJDJ7PWW1P8p0LfFU0i5fsyO7FqRzRB8mkdS4g==", - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.10.1", - "@prisma/engines-version": "6.10.1-1.9b628578b3b7cae625e8c927178f15a170e74a9c", - "@prisma/get-platform": "6.10.1" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.10.1.tgz", - "integrity": "sha512-4CY5ndKylcsce9Mv+VWp5obbR2/86SHOLVV053pwIkhVtT9C9A83yqiqI/5kJM9T1v1u1qco/bYjDKycmei9HA==", - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.10.1" - } - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.9", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", @@ -4106,7 +4051,10 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "dev": true, "license": "MIT", + "optional": true, + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4768,31 +4716,6 @@ "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", "license": "MIT" }, - "node_modules/prisma": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.10.1.tgz", - "integrity": "sha512-khhlC/G49E4+uyA3T3H5PRBut486HD2bDqE2+rvkU0pwk9IAqGFacLFUyIx9Uw+W2eCtf6XGwsp+/strUwMNPw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/config": "6.10.1", - "@prisma/engines": "6.10.1" - }, - "bin": { - "prisma": "build/index.js" - }, - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5622,7 +5545,7 @@ "version": "5.8.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "peer": true, "bin": { diff --git a/public/assets/css/performance.css b/public/assets/css/performance.css index 03efdc2..9123fdf 100644 --- a/public/assets/css/performance.css +++ b/public/assets/css/performance.css @@ -233,12 +233,18 @@ border-bottom: none; } -.ranking-table-inner tr:nth-child(4) td { +.ranking-table-inner tr.highlight-you td { background: #f8dd56; color: #333; font-weight: bold; } +.ranking-table-inner td:nth-child(2) { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} + /* ===== Botão e Modal de Pontuação ===== */ .pontuation-info-btn { position: absolute; diff --git a/src/api/Statistics.jsx b/src/api/Statistics.jsx new file mode 100644 index 0000000..69b4372 --- /dev/null +++ b/src/api/Statistics.jsx @@ -0,0 +1,32 @@ +export const getAllUsers = async () => { + try { + const response = await fetch("http://localhost:3000/users"); + if (!response.ok) { + throw new Error("Failed to fetch users"); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching users:", error); + return []; + } +} + +export const postStatisticsData = async (data) => { + try { + const response = await fetch("http://localhost:3000/statistics/:id", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + if (!response.ok) { + throw new Error("Failed to post statistics data"); + } + return await response.json(); + } catch (error) { + console.error("Error posting statistics data:", error); + return null; + } +} \ No newline at end of file diff --git a/src/pages/Cadastro.jsx b/src/pages/Cadastro.jsx index 8bf0af7..24161de 100644 --- a/src/pages/Cadastro.jsx +++ b/src/pages/Cadastro.jsx @@ -61,7 +61,6 @@ const Cadastro = () => { minLength="3" maxLength="50" title="O nome deve conter apenas letras e espaços" - pattern="[a-z][A-Z]" />
    @@ -75,12 +74,11 @@ const Cadastro = () => { type="text" className="input year-input" id="birthyear" - maxLength="10" + maxLength="4" value={anoNascimento} onChange={(e) => setAnoNascimento(e.target.value)} required title="Digite uma data válida no formato DD/MM/AAAA" - pattern='[0-9]' />
    diff --git a/src/routes/performance.lazy.jsx b/src/routes/performance.lazy.jsx index b4eae53..b868d51 100644 --- a/src/routes/performance.lazy.jsx +++ b/src/routes/performance.lazy.jsx @@ -26,16 +26,16 @@ function PerformanceComponent() { : defaultTodosExer; const pillars = [ - { name: "Decomposição", img: "/img/ana.png", lessons: 8, total: 10, color: "#D5C2E0", }, + { name: "Decomposição", img: "/img/ana.png", lessons: 0, total: 1, color: "#D5C2E0", }, { name: "Reconhecimento de Padrões", img: "/img/lilu.png", - lessons: 6, - total: 10, + lessons: 0, + total: 1, color: "#F8DD56", }, - { name: "Abstração", img: "/img/soso.png", lessons: 9, total: 10, color: "#78BD77" }, - { name: "Algoritmos", img: "/img/cadu.png", lessons: 9, total: 10, color: "#C2DAD6" }, + { name: "Abstração", img: "/img/soso.png", lessons: 0, total: 1, color: "#78BD77" }, + { name: "Algoritmos", img: "/img/cadu.png", lessons: 0, total: 1, color: "#C2DAD6" }, ]; const exercises = [ @@ -68,24 +68,25 @@ function PerformanceComponent() { const [showModal, setShowModal] = useState(false); const ranking = [ - { pos: 1, user: "Ana", score: 950 }, - { pos: 2, user: "João", score: 900 }, - { pos: 3, user: "Maria", score: 850 }, + { pos: 1, user: "Ana Silva", score: 950 }, + { pos: 2, user: "João Souza", score: 900 }, + { pos: 3, user: "Maria Oliveira", score: 850 }, { pos: 4, user: "Você", score: 800 }, - { pos: 5, user: "Lucas", score: 750 }, - { pos: 6, user: "Pedro", score: 700 }, - { pos: 7, user: "Clara", score: 650 }, - { pos: 8, user: "Rafaela", score: 600 }, - { pos: 9, user: "Bruno", score: 550 }, - { pos: 10, user: "Gabriel", score: 500 }, + { pos: 5, user: "Lucas Ferreira", score: 750 }, + { pos: 6, user: "Pedro Almeida", score: 700 }, + { pos: 7, user: "Clara Costa", score: 650 }, + { pos: 8, user: "Rafaela Lima", score: 600 }, + { pos: 9, user: "Bruno Martins", score: 550 }, + { pos: 10, user: "Gabriel Rocha", score: 500 }, ]; const userRow = ranking.find(r => r.user === "Você"); const userPos = userRow ? userRow.pos : 0; let filledStars = 0; if (userPos === 1) filledStars = 3; - else if (userPos === 2 || userPos === 3) filledStars = 2; - else if (userPos === 4 || userPos === 5) filledStars = 1; + else if (userPos === 2) filledStars = 2; + else if (userPos === 3) filledStars = 1; + else filledStars = 0; return ( <> @@ -163,17 +164,28 @@ function PerformanceComponent() {
    -
    -
    -
    {userPos ? `${userPos}º` : "--"}
    +
    +
    +
    + {userPos ? `${userPos}º` : "--"} +
    - - - + {Array(filledStars).fill().map((index) => ( + + ))}
    - {userPos && userPos <= 5 ? "Você está no Top 5!" : "Continue para subir no ranking!"} + {userPos && userPos <= 5 ? `Você está no Top ${userPos}!` : "Continue para subir no ranking!"}
    @@ -220,7 +232,10 @@ function PerformanceComponent() { {ranking.map((row) => ( - + {row.pos} {row.user} {row.score} diff --git a/src/routes/statistics.lazy.jsx b/src/routes/statistics.lazy.jsx index 6a5dd89..5a52a9b 100644 --- a/src/routes/statistics.lazy.jsx +++ b/src/routes/statistics.lazy.jsx @@ -32,7 +32,7 @@ function statisticsComponent() {
  • - {todosExer[0][3]} 50 XP + {todosExer[0][3]} 100 XP

    @@ -61,7 +61,7 @@ function statisticsComponent() {

  • - {todosExer[0][3]} 50 XP + {todosExer[0][3]} 100 XP

    @@ -90,7 +90,7 @@ function statisticsComponent() {

  • - {todosExer[2][3]} 50 XP + {todosExer[2][3]} 100 XP

    @@ -159,7 +159,7 @@ export const Star = ({ width, height, color }) => ( Date: Thu, 3 Jul 2025 19:27:34 -0300 Subject: [PATCH 07/10] fix: vercel deployment 404 --- package.json | 3 ++- vercel.json | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 vercel.json diff --git a/package.json b/package.json index c20ac5b..0611c15 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "scripts": { "test": "", "lint": "eslint", - "dev": "vite" + "dev": "vite", + "vercel-build": "vite build" }, "type": "module", "repository": { diff --git a/vercel.json b/vercel.json new file mode 100644 index 0000000..41be2e9 --- /dev/null +++ b/vercel.json @@ -0,0 +1 @@ +{ "rewrites": [{ "source": "/(.*)", "destination": "/" }] } \ No newline at end of file From febe35cef73a5ca6a4251e5ea15a596bbfb92eb6 Mon Sep 17 00:00:00 2001 From: zlucasftw Date: Thu, 3 Jul 2025 20:23:23 -0300 Subject: [PATCH 08/10] feat: configure environment variables and deploy setup --- DEPLOY_CHECKLIST.md | 59 +++++++++++++++++++++++++++++++++++ README.md | 60 ++++++++++++++++++++++++++++++++++++ SETUP_PRODUCTION_TEST.md | 55 +++++++++++++++++++++++++++++++++ env.example | 16 ++++++++++ index.html | 4 +-- package.json | 1 + src/api/Statistics.jsx | 6 ++-- src/api/User.jsx | 6 ++-- src/config/api.js | 22 +++++++++++++ src/contexts/UserContext.jsx | 3 +- vite.config.js | 4 ++- 11 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 DEPLOY_CHECKLIST.md create mode 100644 SETUP_PRODUCTION_TEST.md create mode 100644 env.example create mode 100644 src/config/api.js diff --git a/DEPLOY_CHECKLIST.md b/DEPLOY_CHECKLIST.md new file mode 100644 index 0000000..272258f --- /dev/null +++ b/DEPLOY_CHECKLIST.md @@ -0,0 +1,59 @@ +# ✅ Checklist de Deploy - Time for Code Frontend + +## 🔒 Segurança + +- [x] **Arquivo .env no .gitignore** - Variáveis sensíveis não serão commitadas +- [x] **Zero URLs hardcoded** - Todas as URLs da API usam variáveis de ambiente +- [x] **Configuração limpa** - Apenas exemplos genéricos nos arquivos de documentação + +## 🚀 Configuração Vercel + +- [x] **vercel.json** - Configurado para SPA (Single Page Application) +- [x] **package.json** - Scripts de build configurados corretamente +- [x] **Build testado** - `npm run build` funciona sem erros + +## 🔧 Variáveis de Ambiente + +### Para Deploy na Vercel: +1. Acesse o dashboard da Vercel +2. Vá em **Settings > Environment Variables** +3. Adicione: + - **Name**: `VITE_API_BASE_URL` + - **Value**: `https://sua-api-producao.com` + - **Environment**: Production (e Preview se desejar) + +## 📁 Arquivos Importantes + +- [x] `src/config/api.js` - Configuração centralizada da API +- [x] `vite.config.js` - Proxy configurado para desenvolvimento +- [x] `vercel.json` - Configuração para SPA +- [x] `package.json` - Scripts de build +- [x] `.gitignore` - Protege arquivos sensíveis +- [x] `README.md` - Documentação atualizada +- [x] `env.example` - Exemplo de configuração + +## 🎯 Próximos Passos + +1. **Commit das alterações:** + ```bash + git add . + git commit -m "feat: configure environment variables and deploy setup" + ``` + +2. **Push para GitHub:** + ```bash + git push origin main + ``` + +3. **Configurar Vercel:** + - Conectar repositório GitHub + - Configurar variável de ambiente + - Deploy automático ativado + +## ✅ Status: PRONTO PARA DEPLOY + +O projeto está configurado corretamente para: +- ✅ Deploy automático na Vercel +- ✅ Uso seguro de variáveis de ambiente +- ✅ Funcionamento em desenvolvimento e produção +- ✅ Zero informações sensíveis no repositório \ No newline at end of file diff --git a/README.md b/README.md index 071fb1b..f61fa95 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,63 @@ O Time for Code está sendo desenvolvido para crianças e adolescentes que estã - **Lucas [(zlucasftw)](https://github.com/zlucasftw)**: Desenvolvedor Backend - **Layla [(laycsz)](https://github.com/laycsz)**: Desenvolvedora Frontend e Designer UI/UX - **Arthur [(ArthurVenturi)](https://github.com/ArthurVenturi)**: Desenvolvedor Frontend + +# Time for Code - Frontend + +## Configuração de Variáveis de Ambiente + +### Desenvolvimento Local + +1. Copie o arquivo `env.example` para `.env`: +```bash +cp env.example .env +``` + +2. Configure a URL da API no arquivo `.env`: + +**Para testar API de produção:** +```bash +VITE_API_BASE_URL=https://sua-api-producao.com +``` + +**Para API local:** +```bash +VITE_API_BASE_URL=http://localhost:3000 +``` + +**Nota:** Em desenvolvimento, o sistema usa proxy do Vite automaticamente para APIs externas. + +### Deploy na Vercel + +1. Acesse o dashboard da Vercel +2. Vá para **Settings > Environment Variables** +3. Adicione a variável: + - **Name**: `VITE_API_BASE_URL` + - **Value**: `https://sua-api-producao.com` + - **Environment**: Production (e Preview se desejar) + +### Vantagens da Configuração + +✅ **Flexível**: Pode usar qualquer URL da API através de variáveis de ambiente +✅ **Seguro**: URLs não ficam hardcoded no código +✅ **Portável**: Funciona em qualquer ambiente (dev, staging, prod) +✅ **Manutenível**: Fácil de mudar URLs sem alterar código + +### Estrutura de Arquivos + +- `src/config/api.js` - Configuração centralizada da API +- `src/api/` - Endpoints da API +- `src/contexts/` - Contextos do React + +## Scripts Disponíveis + +- `npm run dev` - Inicia o servidor de desenvolvimento +- `npm run build` - Gera build de produção +- `npm run vercel-build` - Build específico para Vercel + +## Tecnologias + +- React 19 +- Vite +- TanStack Router +- TanStack Query diff --git a/SETUP_PRODUCTION_TEST.md b/SETUP_PRODUCTION_TEST.md new file mode 100644 index 0000000..93e8f2a --- /dev/null +++ b/SETUP_PRODUCTION_TEST.md @@ -0,0 +1,55 @@ +# Configuração para Testar API de Produção + +## Passo 1: Configurar o arquivo .env + +Edite o arquivo `.env` e configure a URL da API: + +```bash +# Para testar API de produção +VITE_API_BASE_URL=https://sua-api-producao.com + +# Para API local (alternativa) +# VITE_API_BASE_URL=http://localhost:3000 +``` + +## Passo 2: Testar localmente + +Execute o projeto: + +```bash +npm run dev +``` + +O sistema vai automaticamente: +- Usar a URL configurada em `VITE_API_BASE_URL` +- Se for uma URL externa, usar o proxy do Vite para evitar CORS +- Redirecionar `/api/*` para a URL configurada + +## Passo 3: Testar funcionalidades + +1. Acesse: http://localhost:5175 +2. Teste o cadastro de usuário +3. Teste o login +4. Verifique se não há erros de CORS + +## Passo 4: Preparar para Deploy + +Quando estiver funcionando, você pode: + +1. Fazer commit das alterações +2. Fazer push para o GitHub +3. Na Vercel, configurar a variável de ambiente: + - **Name**: `VITE_API_BASE_URL` + - **Value**: `https://sua-api-producao.com` + +## Como funciona: + +- **Desenvolvimento**: Usa proxy do Vite (`/api/*` → `VITE_API_BASE_URL/*`) +- **Produção**: Usa URL direta (`VITE_API_BASE_URL/*`) + +## Vantagens desta configuração: + +✅ **Flexível**: Pode usar qualquer URL da API +✅ **Seguro**: URLs não ficam hardcoded no código +✅ **Portável**: Funciona em qualquer ambiente +✅ **Manutenível**: Fácil de mudar URLs sem alterar código \ No newline at end of file diff --git a/env.example b/env.example new file mode 100644 index 0000000..98daf42 --- /dev/null +++ b/env.example @@ -0,0 +1,16 @@ +# Exemplo de variáveis de ambiente +# Copie este arquivo para .env e configure os valores + +# URL base da API +# Para desenvolvimento local com API local: +VITE_API_BASE_URL=http://localhost:3000 + +# Para desenvolvimento local com API remota: +# VITE_API_BASE_URL=https://sua-api-producao.com + +# Para produção, use a URL da sua API: +# VITE_API_BASE_URL=https://sua-api-producao.com + +# NOTA: O sistema usa variáveis de ambiente para máxima flexibilidade +# Em desenvolvimento, usa proxy do Vite para APIs externas +# Em produção, usa a URL direta configurada na Vercel diff --git a/index.html b/index.html index 8721bb2..8f2ab37 100644 --- a/index.html +++ b/index.html @@ -18,11 +18,11 @@

    Não Renderizado
    - + \ No newline at end of file diff --git a/package.json b/package.json index 0611c15..c7c0b36 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "test": "", "lint": "eslint", "dev": "vite", + "build": "vite build", "vercel-build": "vite build" }, "type": "module", diff --git a/src/api/Statistics.jsx b/src/api/Statistics.jsx index 69b4372..4df9761 100644 --- a/src/api/Statistics.jsx +++ b/src/api/Statistics.jsx @@ -1,6 +1,8 @@ +import { getApiUrl, API_CONFIG } from '../config/api.js'; + export const getAllUsers = async () => { try { - const response = await fetch("http://localhost:3000/users"); + const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.USERS)); if (!response.ok) { throw new Error("Failed to fetch users"); } @@ -14,7 +16,7 @@ export const getAllUsers = async () => { export const postStatisticsData = async (data) => { try { - const response = await fetch("http://localhost:3000/statistics/:id", { + const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.STATISTICS + '/:id'), { method: "POST", headers: { "Content-Type": "application/json", diff --git a/src/api/User.jsx b/src/api/User.jsx index 9df3796..f8bf780 100644 --- a/src/api/User.jsx +++ b/src/api/User.jsx @@ -1,6 +1,8 @@ +import { getApiUrl, API_CONFIG } from '../config/api.js'; + export async function login(email, password) { try { - const response = await fetch("http://localhost:3000/login", { + const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.LOGIN), { method: "POST", credentials: "include", headers: { @@ -24,7 +26,7 @@ export async function login(email, password) { export async function cadastro(nome, anoNascimento, email, senha) { try { - const response = await fetch("http://localhost:3000/register", { + const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.REGISTER), { method: "POST", credentials: "include", headers: { diff --git a/src/config/api.js b/src/config/api.js new file mode 100644 index 0000000..3ef0bf3 --- /dev/null +++ b/src/config/api.js @@ -0,0 +1,22 @@ +// src/config/api.js +export const API_CONFIG = { + BASE_URL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000', + ENDPOINTS: { + LOGIN: '/login', + REGISTER: '/register', + SESSION: '/sesion', + USERS: '/users', + STATISTICS: '/statistics' + } +}; + +export const getApiUrl = (endpoint) => { + const baseUrl = API_CONFIG.BASE_URL; + + // Se a URL é externa e estamos em desenvolvimento, usa proxy + if (import.meta.env.DEV && baseUrl && baseUrl.includes('https://')) { + return `/api${endpoint}`; + } + + return `${baseUrl}${endpoint}`; +}; \ No newline at end of file diff --git a/src/contexts/UserContext.jsx b/src/contexts/UserContext.jsx index d145452..385170e 100644 --- a/src/contexts/UserContext.jsx +++ b/src/contexts/UserContext.jsx @@ -1,4 +1,5 @@ import { createContext, useContext, useState, useEffect } from "react" +import { getApiUrl, API_CONFIG } from '../config/api.js'; //creating a context to store the user data with a default value as null and no function const UserContext = createContext({ @@ -15,7 +16,7 @@ export const UserProvider = ({ children }) => { useEffect(() => { async function fetchUser() { try { - const res = await fetch("http://localhost:3000/sesion", { + const res = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.SESSION), { credentials: "include", // send cookies headers: { "Content-Type": "application/json" diff --git a/vite.config.js b/vite.config.js index 2b9bf92..9ab1f78 100644 --- a/vite.config.js +++ b/vite.config.js @@ -7,8 +7,10 @@ export default defineConfig({ server: { proxy: { '/api': { - target: 'http://localhost:3000', + target: process.env.VITE_API_BASE_URL || 'http://localhost:3000', changeOrigin: true, + secure: true, + rewrite: (path) => path.replace(/^\/api/, ''), }, }, }, From 45caec594e9846eb861dd7b0b34b320c3e013e87 Mon Sep 17 00:00:00 2001 From: zlucasftw Date: Thu, 3 Jul 2025 21:50:45 -0300 Subject: [PATCH 09/10] fix: api connection --- src/api/User.jsx | 52 ++++++++++++++++++++++++++++++++------------- src/config/api.js | 9 ++++++++ src/pages/Login.jsx | 2 +- 3 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/api/User.jsx b/src/api/User.jsx index f8bf780..c8ca1f6 100644 --- a/src/api/User.jsx +++ b/src/api/User.jsx @@ -2,50 +2,72 @@ import { getApiUrl, API_CONFIG } from '../config/api.js'; export async function login(email, password) { try { - const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.LOGIN), { + const url = getApiUrl(API_CONFIG.ENDPOINTS.LOGIN); + console.log('Attempting login to:', url); + + const response = await fetch(url, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email, password }), - }).catch((e) => { throw new Error("Erro de conexão!", e) }); + }); - if (response.status !== 202) { - throw new Error("Login Falhou!"); + console.log('Login response status:', response.status); + console.log('Login response headers:', response.headers); + + if (!response.ok) { + const errorText = await response.text(); + console.error('Login failed with status:', response.status, 'Error:', errorText); + throw new Error(`Login falhou! Status: ${response.status}`); } const data = await response.json(); + console.log('Login successful:', data); return data; - } catch (e) { - console.error("Erro ao fazer login:", e); - throw e; + } catch (error) { + console.error("Erro ao fazer login:", error); + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error("Erro de conexão! Verifique se o servidor está rodando."); + } + throw error; } }; export async function cadastro(nome, anoNascimento, email, senha) { try { - const response = await fetch(getApiUrl(API_CONFIG.ENDPOINTS.REGISTER), { + const url = getApiUrl(API_CONFIG.ENDPOINTS.REGISTER); + console.log('Attempting registration to:', url); + + const response = await fetch(url, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ nome, anoNascimento, email, senha }), - }).catch((e) => { throw new Error("Erro de conexão!", e) }); + }); + + console.log('Registration response status:', response.status); - if (response.status !== 201) { - throw new Error("Cadastro Falhou!"); + if (!response.ok) { + const errorText = await response.text(); + console.error('Registration failed with status:', response.status, 'Error:', errorText); + throw new Error(`Cadastro falhou! Status: ${response.status}`); } const data = await response.json(); - + console.log('Registration successful:', data); return data; - } catch (e) { - console.error("Erro ao fazer o cadastro:", e); - throw e; + } catch (error) { + console.error("Erro ao fazer o cadastro:", error); + if (error.name === 'TypeError' && error.message.includes('fetch')) { + throw new Error("Erro de conexão! Verifique se o servidor está rodando."); + } + throw error; } }; diff --git a/src/config/api.js b/src/config/api.js index 3ef0bf3..08dc2d2 100644 --- a/src/config/api.js +++ b/src/config/api.js @@ -15,8 +15,17 @@ export const getApiUrl = (endpoint) => { // Se a URL é externa e estamos em desenvolvimento, usa proxy if (import.meta.env.DEV && baseUrl && baseUrl.includes('https://')) { + console.log('Using proxy for external API in development'); return `/api${endpoint}`; } + // Se estamos em desenvolvimento e a URL é localhost, usa diretamente + if (import.meta.env.DEV && baseUrl && baseUrl.includes('localhost')) { + console.log('Using direct localhost URL in development'); + return `${baseUrl}${endpoint}`; + } + + // Para produção ou outras configurações + console.log('Using configured API URL:', `${baseUrl}${endpoint}`); return `${baseUrl}${endpoint}`; }; \ No newline at end of file diff --git a/src/pages/Login.jsx b/src/pages/Login.jsx index 9e8b87e..68f1a72 100644 --- a/src/pages/Login.jsx +++ b/src/pages/Login.jsx @@ -57,7 +57,7 @@ const Login = () => { required minLength="5" maxLength="50" - pattern="[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$" + pattern="[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$" title="Digite um e-mail válido" />
    From ec945c3e3a32805f957760912ef7d189c4c4b5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20G=C3=B3is?= <139496125+zlucasftw@users.noreply.github.com> Date: Thu, 3 Jul 2025 22:59:12 -0400 Subject: [PATCH 10/10] fix credentials on fetching --- src/api/User.jsx | 4 +--- vite.config.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/api/User.jsx b/src/api/User.jsx index c8ca1f6..09d7198 100644 --- a/src/api/User.jsx +++ b/src/api/User.jsx @@ -7,11 +7,10 @@ export async function login(email, password) { const response = await fetch(url, { method: "POST", - credentials: "include", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ email, password }), + body: JSON.stringify({ "email": email, "password": password }), }); console.log('Login response status:', response.status); @@ -43,7 +42,6 @@ export async function cadastro(nome, anoNascimento, email, senha) { const response = await fetch(url, { method: "POST", - credentials: "include", headers: { "Content-Type": "application/json", }, diff --git a/vite.config.js b/vite.config.js index 9ab1f78..860e305 100644 --- a/vite.config.js +++ b/vite.config.js @@ -6,7 +6,7 @@ export default defineConfig({ plugins: [TanStackRouterVite(), react()], server: { proxy: { - '/api': { + '/': { target: process.env.VITE_API_BASE_URL || 'http://localhost:3000', changeOrigin: true, secure: true,