diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 82e9f886..4c82eac9 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -49,7 +49,7 @@ app.post('/adduser', async (req, res) => { app.post('/addgame', async (req, res) => { try { - const userResponse = await axios.post(userServiceUrl+'/addgame', req.body); + const userResponse = await axios.post(retrieveServiceUrl+'/addgame', req.body); res.json(userResponse.data); } catch (error) { res.status(error.response.status).json({ error: error.response.data.error }); @@ -60,7 +60,7 @@ app.post('/addgame', async (req, res) => { app.get('/getgamehistory/:username', async (req, res) => { try { const username = req.params.username; - const userResponse = await axios.get(`${userServiceUrl}/getgamehistory/${username}`); + const userResponse = await axios.get(`${retrieveServiceUrl}/getgamehistory/${username}`); res.json(userResponse.data); } catch (error) { res.status(error.response.status).json({ error: error.response.data.error }); @@ -91,6 +91,17 @@ app.get('/getquestionshistory', async (req, res) => { } }); +app.get('/getregisteredusers', async (req, res) => { + try { + // Create a petition to the URL (le llegará a retrieve-service.js) with the option /getregisteredusers and the req.body params + const registeredUsersResponse = await axios.get(userServiceUrl+'/getregisteredusers', req.body); + // Return a json response with what we obtained on the petition + res.json(registeredUsersResponse.data); + } catch (error) { + res.status(error.response.status).json({ error: error.response.data.error }); + } +}); + // Read the OpenAPI YAML file synchronously diff --git a/questions/creationservice/creation-service.js b/questions/creationservice/creation-service.js index 1243145c..ca4c1c3a 100644 --- a/questions/creationservice/creation-service.js +++ b/questions/creationservice/creation-service.js @@ -24,7 +24,7 @@ var randomQuerySelector; // Array of the possible queries var queries = ['SELECT DISTINCT ?questionObject ?questionObjectLabel ?answer ?answerLabel WHERE { ?questionObject wdt:P31 wd:Q6256. ?questionObject wdt:P36 ?answer. SERVICE wikibase:label {bd:serviceParam wikibase:language "[AUTO_LANGUAGE],es".}}']; // Array of the possible questions -var questions = ["¿Cual es la capital de "]; +var questions = ["¿Cuál es la capital de "]; // Recieves the information of the query and select wich data use on the question function getQuestionInfo(info){ diff --git a/users/userservice/playedGame-model.js b/questions/retrieveservice/playedGame-model.js similarity index 95% rename from users/userservice/playedGame-model.js rename to questions/retrieveservice/playedGame-model.js index ef3f07cd..43b1bb13 100644 --- a/users/userservice/playedGame-model.js +++ b/questions/retrieveservice/playedGame-model.js @@ -3,7 +3,7 @@ const mongoose = require('mongoose'); const gameSchema = new mongoose.Schema({ username: { type: String, required: true }, duration: Number, - questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Question' }], + questions: [{ type: mongoose.Schema.Types.ObjectId, ref: 'QuestionAnswered' }], date: { type: Date, default: Date.now } , percentage: Number, totalQuestions: Number, diff --git a/users/userservice/question-model.js b/questions/retrieveservice/question-model.js similarity index 59% rename from users/userservice/question-model.js rename to questions/retrieveservice/question-model.js index 80b3edcd..94fd1a3b 100644 --- a/users/userservice/question-model.js +++ b/questions/retrieveservice/question-model.js @@ -6,6 +6,6 @@ const questionSchema = new mongoose.Schema({ userAnswer: String }); -const Question = mongoose.model('Question', questionSchema); +const QuestionAnswered = mongoose.model('QuestionAnswered', questionSchema); -module.exports = Question; +module.exports = QuestionAnswered diff --git a/questions/retrieveservice/retrieve-service.js b/questions/retrieveservice/retrieve-service.js index 0d65c42a..c0b233f4 100644 --- a/questions/retrieveservice/retrieve-service.js +++ b/questions/retrieveservice/retrieve-service.js @@ -1,6 +1,8 @@ const express = require('express'); const mongoose = require('mongoose'); const Question = require('./questionshistory-model') +const Game = require('./playedGame-model') +const QuestionAnswered = require('./question-model') const app = express(); const port = 8004; @@ -22,6 +24,62 @@ app.get('/getquestionshistory', async (req, res) => { res.status(200).json(solution); }); +app.post('/addgame', async (req, res) => { + try { + // Obtener los datos del juego desde el cuerpo de la solicitud + const gameData = req.body; + + // Convertir las preguntas del juego en ObjectId + const questionIds = await Promise.all(gameData.questions.map(async (question) => { + const existingQuestion = await QuestionAnswered.findOne({ + question: question.question, + correctAnswer: question.correctAnswer, + userAnswer: question.userAnswer + }); + if (existingQuestion) { + return existingQuestion._id; + } else { + const newQuestion = new QuestionAnswered(question); + await newQuestion.save(); + return newQuestion._id; + } + })); + + // Reemplazar las preguntas en el juego con sus ObjectId + gameData.questions = questionIds; + + // Crear una nueva instancia del modelo de juego con los datos proporcionados + const newGame = new Game(gameData); + + // Guardar el nuevo juego en la base de datos + await newGame.save(); + + // Enviar una respuesta de éxito + res.status(200).json({ message: "Partida guardada exitosamente" }); + } catch (error) { + // Manejar errores y enviar una respuesta de error con el mensaje de error + console.error("Error al guardar el juego:", error); + res.status(400).json({ error: error.message }); + } +}); + + + +app.get('/getgamehistory/:username', async (req, res) => { + try { + const username = req.params.username; + console.log("Se está intentando encontrar el historial del usuario " + username); + // Buscar las partidas asociadas al nombre de usuario proporcionado + const games = await Game.find({ username }).populate('questions'); + console.log("Se encontraron los juegos para " + username + ": ", games); + res.json(games); + } catch (error) { + res.status(400).json({ + error: error.message + }); + } +}); + const server = app.listen(port, () => { console.log(`Creation Service listening at http://localhost:${port}`); }); diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index 9f083dba..711d49dd 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -4,8 +4,6 @@ const mongoose = require('mongoose'); const bcrypt = require('bcrypt'); const bodyParser = require('body-parser'); const User = require('./user-model') -const Game = require('./playedGame-model') -const Question = require('./question-model') const app = express(); const port = 8001; @@ -50,60 +48,15 @@ app.post('/adduser', async (req, res) => { } }); -app.post('/addgame', async (req, res) => { - try { - // Obtener los datos del juego desde el cuerpo de la solicitud - const gameData = req.body; - - // Convertir las preguntas del juego en ObjectId - const questionIds = await Promise.all(gameData.questions.map(async (question) => { - const existingQuestion = await Question.findOne({ - question: question.question, - correctAnswer: question.correctAnswer, - userAnswer: question.userAnswer - }); - if (existingQuestion) { - return existingQuestion._id; - } else { - const newQuestion = new Question(question); - await newQuestion.save(); - return newQuestion._id; - } - })); - - // Reemplazar las preguntas en el juego con sus ObjectId - gameData.questions = questionIds; - - // Crear una nueva instancia del modelo de juego con los datos proporcionados - const newGame = new Game(gameData); - - // Guardar el nuevo juego en la base de datos - await newGame.save(); - - // Enviar una respuesta de éxito - res.status(200).json({ message: "Partida guardada exitosamente" }); - } catch (error) { - // Manejar errores y enviar una respuesta de error con el mensaje de error - console.error("Error al guardar el juego:", error); - res.status(400).json({ error: error.message }); - } -}); - +app.get('/getregisteredusers', async (req, res) => { + const registeredUsers = await User.find({}); + var solution = []; + registeredUsers.forEach(row => { + solution.push([row.username,new Date(row.createdAt).toLocaleDateString()]); + }); -app.get('/getgamehistory/:username', async (req, res) => { - try { - const username = req.params.username; - console.log("Se está intentando encontrar el historial del usuario " + username); - // Buscar las partidas asociadas al nombre de usuario proporcionado - const games = await Game.find({ username }).populate('questions'); - console.log("Se encontraron los juegos para " + username + ": ", games); - res.json(games); - } catch (error) { - res.status(400).json({ - error: error.message - }); - } + res.status(200).json(solution); }); const server = app.listen(port, () => { diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 658de7d2..780d7e56 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -13,7 +13,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", + "animate.css": "^4.1.1", "axios": "^1.6.5", + "bootstrap": "^5.3.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.1", @@ -6589,6 +6591,11 @@ "ajv": "^6.9.1" } }, + "node_modules/animate.css": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz", + "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==" + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -7505,6 +7512,24 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, + "node_modules/bootstrap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", + "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/boxen": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", diff --git a/webapp/package.json b/webapp/package.json index 18a15f71..0b85e3cd 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -9,7 +9,9 @@ "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", + "animate.css": "^4.1.1", "axios": "^1.6.5", + "bootstrap": "^5.3.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.22.1", diff --git a/webapp/public/questions-illustration.png b/webapp/public/questions-illustration.png new file mode 100644 index 00000000..1595326d Binary files /dev/null and b/webapp/public/questions-illustration.png differ diff --git a/webapp/public/questions-video.mp4 b/webapp/public/questions-video.mp4 new file mode 100644 index 00000000..97a73cd6 Binary files /dev/null and b/webapp/public/questions-video.mp4 differ diff --git a/webapp/src/App.css b/webapp/src/App.css index 60638c44..6524432d 100644 --- a/webapp/src/App.css +++ b/webapp/src/App.css @@ -43,3 +43,5 @@ transform: rotate(360deg); } } + + diff --git a/webapp/src/App.js b/webapp/src/App.js index 50a60ea3..1d30bc23 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -19,6 +19,9 @@ function App() { +
+ + Bienvenido a WIQ 2024 del curso de Arquitectura del Software @@ -35,6 +38,7 @@ function App() { )} +
); diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index 7137d882..49df120a 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -1,10 +1,13 @@ import React, { useState, useEffect } from 'react'; import axios from 'axios'; -import { Container, Typography, Button, Paper} from '@mui/material'; +import { Container, Typography, Button, Paper, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions} from '@mui/material'; + import { useNavigate } from 'react-router-dom'; import './Game.css'; +import '../index.css'; + import '../Timer.css'; const colorPreguntas= 'rgba(51, 139, 173, 0.764)'; @@ -44,30 +47,66 @@ const Game = () => { const [time, setTime] = useState(20); const [isTimedOut, setTimedOut] = useState(false); + // Estado para controlar si el temporizador está activo o no const [isTimerActive, setIsTimerActive] = useState(true); + + + const [openDialog, setOpenDialog] = useState(false); + + const handleDialogOpen = () => { + setIsTimerActive(false); + setOpenDialog(true); + }; + + const handleDialogClose = () => { + setIsTimerActive(true); + setOpenDialog(false); + runTimer(); + }; + + + const runTimer = () => { + // Calcular el tiempo restante para el temporizador + const remainingTime = time; + setTime(remainingTime); + setIsTimerActive(true); + }; + + + useEffect(() => { + if (openDialog) { + stopTimer(); + } else { + runTimer(); + } + }); + + + + + useEffect(() => { const id = setInterval(() => { - if (isTimerActive) { // Solo decrementa el tiempo si el temporizador está activo - setTime((prev) => { - if (prev > 0) { - return prev - 1; - } else { - // Se acabó el tiempo - setTimedOut(true); - const buttons = document.querySelectorAll('button[title="btnsPreg"]'); - buttons.forEach(button => { - button.disabled = true; - button.onmouse = null; - }); - } - }); - } + setTime(prev => { + if (prev > 0) { + return prev - 1; + } else { + setTimedOut(true); + const buttons = document.querySelectorAll('button[title="btnsPreg"]'); + buttons.forEach(button => { + button.disabled = true; + button.onmouse = null; + }); + clearInterval(id); // Clear the interval when the time runs out + } + }); }, 1000); - - return () => clearInterval(id); - }, [isTimerActive]); + + return () => clearInterval(id); // Clear the interval on component unmount + }, [isTimerActive, isTimedOut]); + // Calcular el porcentaje de tiempo transcurrido para el círculo del temporizador @@ -79,13 +118,19 @@ const Game = () => { setIsTimerActive(false); }; + + + + // Activar el temporizador const restartTimer = () => { setTime(20); // Reiniciar el tiempo a 20 segundos setIsTimerActive(true); + setTimedOut(false); }; + useEffect(() => { handleShowQuestion(); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -103,16 +148,20 @@ const Game = () => { useEffect(() => { if (isGameFinished() && !isFinished){ - finishGame(); - setFinished(true); + setTimeout(() => { + finishGame(); + setFinished(true); + }, 1000); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [correctCounter]); useEffect(() => { if (isGameFinished() && !isFinished){ - finishGame(); - setFinished(true); + setTimeout(() => { + finishGame(); + setFinished(true); + }, 4000); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [incorrectCounter]); @@ -120,6 +169,8 @@ const Game = () => { // This method will call the create question service const handleShowQuestion = async () => { try{ + setIsTimerActive(false); + // It makes a petition to the api and store the response const response = await axios.get(`${apiEndpoint}/createquestion`, { }); // Extract all the info of the response and store it @@ -144,20 +195,26 @@ const Game = () => { incrementQuestion(); - // Resetear temporizador a 20 segundos - restartTimer(); + + }catch (error){ console.error('Error:', error); - } + } + + // Poner temporizador a 20 segundos + restartTimer(); + + } // Method that checks if the answer clicked is the correct one const handleAnswerClick = (option, index) => { + // Detener el temporizador + setIsTimerActive(false); + // Almacenar la opción seleccionada por el usuario en gameUserOptions setGameUserOptions(prevUserOptions => [...prevUserOptions, option]); - // parar el temporizador - stopTimer(); if(option === correctOption) { const buttonId = `button_${index}`; const correctButton = document.getElementById(buttonId); @@ -175,8 +232,6 @@ const Game = () => { const buttonIdCorrect = `button_${correctIndex}`; const correctButton = document.getElementById(buttonIdCorrect); - console.log("BOTON A COMPROBAR: " + correctButton.textContent); - if (correctButton.textContent === correctOption) { correctButton.style.backgroundColor = "rgba(79, 141, 18, 0.726)"; } @@ -191,7 +246,6 @@ const Game = () => { button.onmouse = null; }); - decrementQuestionsToAnswer(); if (!isGameFinished()) { @@ -290,7 +344,40 @@ const getQuestions = () => { } + + useEffect(() => { + if (isTimedOut && !isFinished) { + // mostrar la respuesta correcta + for (let correctIndex = 0; correctIndex < 4; correctIndex++){ + const buttonIdCorrect = `button_${correctIndex}`; + const correctButton = document.getElementById(buttonIdCorrect); + + if (correctButton.textContent === correctOption) { + correctButton.style.backgroundColor = "rgba(79, 141, 18, 0.726)"; + } + } + + incrementIncorrect(); + decrementQuestionsToAnswer(); + + setTimeout(() => { + if (!isGameFinished()) { + setTimeout(() => { + handleShowQuestion(); + }, 1000); + } + + }, 2000); + + + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isTimedOut]); + + return ( + + {!isFinished && ( @@ -315,7 +402,8 @@ const getQuestions = () => { { )} - {isTimedOut && ( - - - ¡Tiempo agotado! - - - - )} - - - - Pregunta {questionCounter}: {questionObject}
{answerOptions.map((option, index) => ( - + + ))}
@@ -401,19 +469,49 @@ const getQuestions = () => {
-
+
+ +
)} + {!isGameFinished() && !isFinished &&( +
+ +
+ )} + + + Confirmación + + + ¿Estás seguro de que deseas volver al menú principal? Perderás el progreso actual de la partida. + + + + + + + +
); }; diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index 478b48be..084d4b25 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -1,4 +1,3 @@ -// src/components/Login.js import React, { useState } from 'react'; import axios from 'axios'; import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; @@ -10,21 +9,16 @@ const Login = () => { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); - const [loginSuccess, setLoginSuccess] = useState(false); const [openSnackbar, setOpenSnackbar] = useState(false); - // Declara las variables (izquierda) y el metodo que la modifica (derecha). Se inicializa a false (useState) const navigate = useNavigate(); - - const loginUser = async () => { try { await axios.post(`${apiEndpoint}/login`, { username, password }); - localStorage.setItem('username',username); - setLoginSuccess(true); - + localStorage.setItem('username', username); setOpenSnackbar(true); + navigate("/MainPage"); } catch (error) { setError(error.response.data.error); } @@ -36,39 +30,33 @@ const Login = () => { return ( - {/* Los operadores logicos funcionan de la manera: - condicion ? (lo que se hace si se cumple) : (lo que se hace si no se cumple) */} - {loginSuccess ? ( - navigate("/MainPage") - ) : ( -
- - Inicio de sesión - - setUsername(e.target.value)} - /> - setPassword(e.target.value)} - /> - - - {error && ( - setError('')} message={`Error: ${error}`} /> - )} -
- )} +
+ + Inicio de sesión + + setUsername(e.target.value)} + /> + setPassword(e.target.value)} + /> + + + {error && ( + setError('')} message={`Error: ${error}`} /> + )} +
); }; diff --git a/webapp/src/components/MainPage.css b/webapp/src/components/MainPage.css index c210f19c..dee8e995 100644 --- a/webapp/src/components/MainPage.css +++ b/webapp/src/components/MainPage.css @@ -1,12 +1,72 @@ -div[title="main"]{ +div[title="main"] { display: grid; grid-template-columns: 1fr; } +div[title="main-title"] > h1 { + margin: 1.5rem; + color: rgb(24, 46, 63); + font-size: 3rem; + font-weight: 400; +} + +div[title="main-title"] > h2 { + margin: -2rem; + color: #4c8dbf; + font-size: 4rem; + font-weight: bold; + font-family: Verdana, Geneva, Tahoma, sans-serif; +} + + +@keyframes slide { + from { + transform: translateX(0px); + } + to { + transform: translateX(20px); + } +} + + +div[title="main"]>button:hover { + background-color: rgb(189, 216, 255); + color: #007dfe; + font-weight: 600; + width: auto; + font-size: 1em; + height: 3rem; +} + div[title="main"]>button{ - margin: 1em; - padding: 0.5em; - background-color: rgba(31, 60, 134, 0.764); + background-color: #0155B7; color: white; + font-weight: 600; + width: auto; font-size: 1em; -} \ No newline at end of file + height: 3rem; + + margin: 1rem; +} + + + +.img-container { + text-align: center; + margin-bottom: 20px; /* Ajusta el margen inferior según sea necesario */ +} + +.img-container img { + width: 75%; + height: auto; + animation: slide 3s ease infinite alternate; +} + +@media (min-width: 768px) { + .img-container { + text-align: left; + } +} + + + diff --git a/webapp/src/components/MainPage.js b/webapp/src/components/MainPage.js index 757bf60a..9d076d21 100644 --- a/webapp/src/components/MainPage.js +++ b/webapp/src/components/MainPage.js @@ -1,50 +1,74 @@ -import React, { } from 'react'; -import { Container, Typography, Button } from '@mui/material'; +import React from 'react'; +import { Container, Typography, Button, Grid } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import './MainPage.css'; +import Navbar from './Navbar'; + const MainPage = () => { const navigate = useNavigate(); const handleShowGame = () => { - let path= '/Game'; + let path = '/Game'; navigate(path); }; const handleShowHistoricalData = () => { - let path= '/HistoricalData'; + let path = '/HistoricalData'; navigate(path); }; const handleShowHistoricalUserData = () => { - let path= '/HistoricalUserData'; + let path = '/HistoricalUserData'; navigate(path); }; + const handleShowRegisteredUsers = () => { + let path = '/RegisteredUsers'; + navigate(path); + }; return ( - -
- - ¡Bienvenido a WIQ 2024! - + <> + - - Puedes comenzar la partida o ver tu historial. +
+ + ¡Bienvenido a + + + WIQ 2024! - - - -
- + + + + + +
+ Imagen de prueba +
+
+ +
+ + + + +
+
+
+
+ ) } -export default MainPage; \ No newline at end of file +export default MainPage; diff --git a/webapp/src/components/Navbar.css b/webapp/src/components/Navbar.css new file mode 100644 index 00000000..8323e580 --- /dev/null +++ b/webapp/src/components/Navbar.css @@ -0,0 +1,25 @@ +.nav-link { + color: #4c8dbf; + font-weight: 400; +} + +.nav-link:hover { + color: #BCC3C5; + font-weight: 400; +} + +.navbar-brand { + color: #0c3667; + font-weight: 900; + font-size: 2rem; +} + +nav, .dropdown-menu, .dropdown-item { + background-color: #4c8dbf; + border: none; +} + + +.dropdown-item { + color: #4c8dbf; +} diff --git a/webapp/src/components/Navbar.js b/webapp/src/components/Navbar.js new file mode 100644 index 00000000..661a54dd --- /dev/null +++ b/webapp/src/components/Navbar.js @@ -0,0 +1,46 @@ +import React, { useState } from 'react'; +import { Link } from 'react-router-dom'; +import './Navbar.css'; + +const Navbar = () => { + const [historialDropdownOpen, setHistorialDropdownOpen] = useState(false); + + const toggleHistorialDropdown = () => { + setHistorialDropdownOpen(!historialDropdownOpen); + }; + + return ( + + ); +}; + +export default Navbar; diff --git a/webapp/src/components/RegisteredUsers.js b/webapp/src/components/RegisteredUsers.js new file mode 100644 index 00000000..0ebbe269 --- /dev/null +++ b/webapp/src/components/RegisteredUsers.js @@ -0,0 +1,65 @@ +import axios from 'axios'; +import React, { useState, useEffect } from 'react'; +import { useNavigate} from 'react-router-dom'; +import { Container, Button} from '@mui/material'; + +const RegisteredUsers = () => { + const navigate = useNavigate(); + const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + + const [registeredUsers, setRegisteredUsers] = useState([]); + + useEffect(() => { + handleShowHistory(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const handleShowHistory = async () => { + try{ + // It makes a petition to the api and store the response + const response = await axios.get(`${apiEndpoint}/getregisteredusers`, { }); + setRegisteredUsers(response.data); + }catch (error){ + console.error('Error:', error); + } + } + + const handlePreviousPage = async () => { + let path= '/MainPage'; + navigate(path); + } + + return ( + + + +
+ +
+
+ + + + + + + + + {registeredUsers.map((row, rowIndex) => ( + + {row.map((cell, cellIndex) => ( + + ))} + + ))} + +
Nombre de usuarioFecha de registro
{cell}
+
+
+ + ); +}; + +export default RegisteredUsers; \ No newline at end of file diff --git a/webapp/src/index.css b/webapp/src/index.css index ec2585e8..19f9c524 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -1,3 +1,10 @@ +html, body, main, #root { + background-color: #a5b8d4; + overflow: hidden; +} + + + body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', @@ -5,9 +12,16 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + overflow: hidden; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + + +div[title="main"]>h1{ + color: white; +} + diff --git a/webapp/src/index.js b/webapp/src/index.js index 9f794123..b3b8cc0f 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -2,23 +2,30 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import reportWebVitals from './reportWebVitals'; import {Route, Routes, MemoryRouter as Router} from "react-router-dom"; -import './index.css'; import App from './App'; import Game from './components/Game'; import HistoricalData from './components/HistoricalData'; import MainPage from './components/MainPage'; import HistoricalUserData from './components/HistoricalUserData'; +import RegisteredUsers from './components/RegisteredUsers'; + +import './index.css'; +import 'animate.css'; +import 'bootstrap/dist/css/bootstrap.min.css'; + const root = ReactDOM.createRoot(document.getElementById('root')); root.render( + }> }> }> }> }> + }>