diff --git a/gatewayservice/openapi.yaml b/gatewayservice/openapi.yaml index a74fcb42..7d04b2bd 100644 --- a/gatewayservice/openapi.yaml +++ b/gatewayservice/openapi.yaml @@ -98,11 +98,11 @@ paths: example: error: Username already exists. Please choose a different username. - /userInfo: + /userInfo/{username}: get: summary: Get User Information parameters: - - in: query + - in: path name: username schema: type: string @@ -136,6 +136,89 @@ paths: schema: $ref: "#/components/schemas/Error" + /userGames: + get: + summary: Get User Games + parameters: + - in: query + name: username + schema: + type: string + description: Username for which to retrieve games + responses: + "200": + description: Games retrieved successfully + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Game" + example: + - gameMode: clasico + correctAnswers: 10 + incorrectAnswers: 5 + points: 10 + avgTime: 10 + _id: 60b6f7b3b3b3b3b3b3b3b3b3 + questions: + - pregunta: ¿Cuál es la capital de Francia? + respuestas: + - "Londres" + - "Madrid" + - "Berlín" + - "París" + correcta: "París" + respuesta: "París" + - pregunta: ¿Cuál es el río más largo del mundo? + respuestas: + - "Amazonas" + - "Nilo" + - "Mississippi" + - "Yangtsé" + correcta: "Amazonas" + respuesta: "Nilo" + - gameMode: bateria + correctAnswers: 2 + incorrectAnswers: 12 + points: 15 + avgTime: 7 + _id: 65ea02cf891b09479a383477 + questions: + - pregunta: ¿Quién pintó la Mona Lisa? + respuestas: + - "Leonardo da Vinci" + - "Pablo Picasso" + - "Vincent van Gogh" + - "Claude Monet" + correcta: "Leonardo da Vinci" + respuesta: "Leonardo da Vinci" + - pregunta: ¿Cuál es el elemento más abundante en la Tierra? + respuestas: + - "Oxígeno" + - "Hidrógeno" + - "Nitrógeno" + - "Hierro" + correcta: "Oxígeno" + respuesta: "Hidrógeno" + + "400": + description: Username is not present + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Invalid name + "404": + description: User is not present in the database + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: User not found + /saveGameList: post: summary: Save Game List @@ -180,6 +263,201 @@ paths: $ref: "#/components/schemas/Error" example: error: Usuario no encontrado + + /group/list: + get: + summary: Get Group List + responses: + "200": + description: Group list retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/GroupList" + example: + groups: + - _id: 60b6f7b3b3b3b3b3b3b3b3b3 + name: testGroup + createdAt: 2021-06-01T00:00:00Z + members: + - test1 + - test2 + - test3 + __v: 0 + - _id: 60b6f7b3b3b3b3b3b3b3b3b3 + name: testGroup2 + createdAt: 2021-06-01T00:00:00Z + members: + - test4 + - test5 + - test6 + __v: 0 + + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Internal Server Error + + /group/add: + post: + summary: Create Group + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + username: + type: string + description: Username of the group creator + name: + type: string + description: Name of the group to create + responses: + "200": + description: Group created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/SuccessMessage" + example: + message: Group created successfully + "400": + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + EmptyGroupName: + value: + error: Group name cannot be empty + GroupNameExists: + value: + error: Group name already exists + + "404": + description: User is not present in the database + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: User not found + "500": + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Internal Server Error + + /group/{groupName}: + get: + summary: Get Group Information + parameters: + - in: path + name: groupName + schema: + type: string + description: Name of the group for which to retrieve information + responses: + "200": + description: Group information retrieved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/GroupInfo" + examples: + GroupExample: + summary: Example of group information retrieval + value: + group: + _id: "661e8828d59b7c02682d24bc" + name: "Manzana" + createdAt: "2024-04-16T14:16:08.328Z" + members: + - "admin" + - "vinuesa" + __v: 1 + "404": + description: Group not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Group not found + "500": + description: Internal Server Error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Internal Server Error + + /group/join: + post: + summary: Join Group + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + groupId: + type: string + description: ID of the group to join + name: + username: string + description: Username of the user joining the group + responses: + "200": + description: Group created successfully + content: + application/json: + schema: + $ref: "#/components/schemas/SuccessMessage" + example: + message: User joined the group successfully + "400": + description: User already a member of this group + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: User already a member of this group + "404": + description: Resource not found + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + examples: + UserNotFound: + value: + error: User not found in the database + GroupNotFound: + value: + error: Group not found in the database + "500": + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Internal Server Error + /friends: get: summary: Get Friends of User @@ -218,6 +496,79 @@ paths: example: error: Internal Server Error + /users: + get: + summary: Get all users + responses: + "200": + description: Users retrieved successfully + content: + application/json: + schema: + type: array + $ref: "#/components/schemas/UserInfo" + example: + - _id: 60b6f7b3b3b3b3b3b3b3b3b3 + username: testUsername + createdAt: 2021-06-01T00:00:00Z + games: [] + - _id: 60b6f7b3b3b3b3b3b3b3b3b3 + username: testUsername2 + createdAt: 2021-06-01T00:00:00Z + games: + - correctAnswers: 1 + incorrectAnswers: 1 + points: 1 + avgTime: 10 + _id: 60b6f7b3b3b3b3b3b3b3b3b3 + questions: + - pregunta: ¿Cuál es la capital de Francia? + respuestas: + - "Londres" + - "Madrid" + - "Berlín" + - "París" + correcta: "París" + respuesta: "París" + - pregunta: ¿Cuál es el río más largo del mundo? + respuestas: + - "Amazonas" + - "Nilo" + - "Mississippi" + - "Yangtsé" + correcta: "Amazonas" + respuesta: "Nilo" + - correctAnswers: 1 + incorrectAnswers: 1 + points: 1 + avgTime: 2 + _id: 65ea02cf891b09479a383477 + questions: + - pregunta: ¿Quién pintó la Mona Lisa? + respuestas: + - "Leonardo da Vinci" + - "Pablo Picasso" + - "Vincent van Gogh" + - "Claude Monet" + correcta: "Leonardo da Vinci" + respuesta: "Leonardo da Vinci" + - pregunta: ¿Cuál es el elemento más abundante en la Tierra? + respuestas: + - "Oxígeno" + - "Hidrógeno" + - "Nitrógeno" + - "Hierro" + correcta: "Oxígeno" + respuesta: "Hidrógeno" + "500": + description: Invalid request + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + example: + error: Internal Server Error + /users/search: get: summary: Search Users @@ -686,6 +1037,9 @@ components: Game: type: object properties: + gameMode: + type: string + enum: [clasico, bateria, calculadora] correctAnswers: type: integer incorrectAnswers: @@ -696,15 +1050,28 @@ components: type: number _id: type: string - - QuestionList: - type: array - items: - type: object + questions: + type: array + items: + $ref: "#/components/schemas/Question" Question: type: object properties: + pregunta: + type: string + description: Question text + respuestas: + type: array + description: List of possible answers + items: + type: string + correcta: + description: Correct answer + type: string + respuesta: + description: User's answer + type: string Stats: type: object @@ -804,3 +1171,53 @@ components: type: array items: $ref: "#/components/schemas/User" + + GroupInfo: + Group: + type: Group + properties: + _id: + type: string + description: ID del grupo + name: + type: string + description: Nombre del grupo + createdAt: + type: string + format: date-time + description: Fecha de creación del grupo + members: + type: array + items: + type: string + description: Lista de miembros del grupo + __v: + type: integer + description: Versión del esquema + + GroupList: + type: object + properties: + groups: + type: array + items: + $ref: "#/components/schemas/Group" + + Group: + type: object + properties: + _id: + type: string + description: Group ID + name: + type: string + description: Group name + createdAt: + type: string + format: date-time + description: Group creation date + members: + type: array + items: + type: string + description: Group members diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index fe96feca..fa9e6d51 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -100,8 +100,7 @@ app.post("/adduser", async (req, res) => { // Route to get all users app.get("/users", async (req, res) => { try { - const users = await User.find(); - console.log(users); + const users = await User.find({}, { password: 0}); res.status(200).json(users); } catch (error) { res.status(500).json({ error: "Internal Server Error" }); @@ -246,7 +245,7 @@ app.get("/userGames", async (req, res) => { try { const username = req.query.user; if(!username){ - return res.status(400).json({ error: "Nombre inválido" }); + return res.status(400).json({ error: "Invalid name" }); } const user = await User.findOne({ username: username, @@ -309,7 +308,7 @@ app.get('/group/:groupName', async (req, res) => { res.status(200).json({ group }); } catch (error) { - res.status(400).json({ error: error.message }); + res.status(500).json({ error: error.message }); } }); @@ -343,7 +342,7 @@ app.post('/group/add', async (req, res) => { res.status(200).json({ message: 'Group created successfully' }); } catch (error) { - res.status(400).json({ error: error.message }); + res.status(500).json({ error: error.message }); } }); @@ -374,7 +373,7 @@ app.post('/group/join', async (req, res) => { res.status(200).json({ message: 'User joined the group successfully' }); } catch (error) { - res.status(400).json({ error: error.message }); + res.status(500).json({ error: error.message }); } }); diff --git a/users/userservice/user-service.test.js b/users/userservice/user-service.test.js index 87cb723e..e3f9beb0 100644 --- a/users/userservice/user-service.test.js +++ b/users/userservice/user-service.test.js @@ -291,7 +291,7 @@ describe("User Service", () => { }; const response = await request(app).post("/group/add").send(group); - expect(response.status).toBe(400); + expect(response.status).toBe(500); expect(response.body).toEqual({ error: "Missing required field: username" }); }); diff --git a/webapp/src/App.js b/webapp/src/App.js index b19050dd..3c7c44ac 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -48,7 +48,7 @@ function App() { } /> } /> - } /> + } /> } /> } /> diff --git a/webapp/src/App.test.js b/webapp/src/App.test.js index df54d25c..d50ea5ec 100644 --- a/webapp/src/App.test.js +++ b/webapp/src/App.test.js @@ -137,7 +137,7 @@ describe("Nav Component", () => { const perfilButton = screen.getByText("Mi perfil"); fireEvent.click(perfilButton); - expect(window.location.pathname).toBe("/perfil/testuser"); + expect(window.location.pathname).toBe("/perfil"); }); test("navigates to /sobre when Sobre nosotros button is clicked", () => { diff --git a/webapp/src/components/CustomModal/CustomModal.js b/webapp/src/components/CustomModal/CustomModal.js index 31f1706e..024782ae 100644 --- a/webapp/src/components/CustomModal/CustomModal.js +++ b/webapp/src/components/CustomModal/CustomModal.js @@ -1,11 +1,14 @@ import React from 'react'; import { Button, Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react'; import { useNavigate } from 'react-router-dom'; +import { useTranslation } from "react-i18next"; const CustomModal = ({ title, text, route }) => { const { isOpen, onOpen, onClose } = useDisclosure(); const navigate = useNavigate(); + const { t } = useTranslation(); + return ( <> @@ -21,9 +24,9 @@ const CustomModal = ({ title, text, route }) => { - + diff --git a/webapp/src/components/Login/Login.js b/webapp/src/components/Login/Login.js index de509c01..cadbdf7d 100644 --- a/webapp/src/components/Login/Login.js +++ b/webapp/src/components/Login/Login.js @@ -32,7 +32,6 @@ const Login = () => { const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || "http://localhost:8000"; - console.log(process.env.REACT_APP_API_ENDPOINT); const loginUser = () => { axios @@ -50,7 +49,6 @@ const Login = () => { navigate("/home"); }) .catch((err) => { - console.log(err); setError(err.response.data.error); }); }; diff --git a/webapp/src/components/Nav/Nav.js b/webapp/src/components/Nav/Nav.js index 3a14fb6c..7b00d446 100644 --- a/webapp/src/components/Nav/Nav.js +++ b/webapp/src/components/Nav/Nav.js @@ -246,7 +246,7 @@ const Nav = () => { - handleNavigate(`/perfil/${username}`)}> + handleNavigate("/perfil")}> {t("components.nav.myprofile")} handleNavigate("/history")}> diff --git a/webapp/src/components/Profile/Profile.js b/webapp/src/components/Profile/Profile.js index c4816a6c..a7ffa5f0 100644 --- a/webapp/src/components/Profile/Profile.js +++ b/webapp/src/components/Profile/Profile.js @@ -1,20 +1,19 @@ import { Box, VStack, Heading, Text, Center, Spinner, Table, Thead, Tbody, Tr, Th, Td, Avatar } from "@chakra-ui/react"; import React, { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import { useParams } from "react-router-dom"; -const Perfil = () => { +const Profile = (user) => { const gatewayUrl = process.env.REACT_APP_API_ENDPOINT || "http://localhost:8000"; const [userData, setUserData] = useState(null); const [loading, setLoading] = useState(true); - const params = useParams(); - const user = params.user || localStorage.getItem("username"); const [error, setError] = useState(null); const { t } = useTranslation(); + const username = (user && user.username) || localStorage.getItem("username"); + useEffect(() => { - fetch(gatewayUrl + `/userInfo/${encodeURIComponent(user)}`) + fetch(gatewayUrl + `/userInfo/${username}`) .then((response) => response.json()) .then((data) => { setUserData(data); @@ -45,7 +44,7 @@ const Perfil = () => { <> {userData && ( <> - + {t('components.profile.name')} {userData.username} @@ -56,7 +55,7 @@ const Perfil = () => { {t('components.profile.recentGames')} - + { userData.games && userData.games.length > 0 ? ( @@ -95,5 +94,5 @@ const Perfil = () => { ); }; -export default Perfil; +export default Profile; diff --git a/webapp/src/locales/en.json b/webapp/src/locales/en.json index 11b00d8c..0879d619 100644 --- a/webapp/src/locales/en.json +++ b/webapp/src/locales/en.json @@ -1,5 +1,9 @@ { "components": { + "custommodal": { + "play": "Jugar", + "close": "Cerrar" + }, "footer": { "copyright": "Copyright 2024 ® Software Architecture Group 1A" }, @@ -87,7 +91,8 @@ "playAgain": "Play Again", "back": "Back to Menu", "question": "Question", - "time": "Time Remaining:" + "time": "Time Remaining:", + "send": "Send" }, "config": { "title": "Settings", diff --git a/webapp/src/locales/es.json b/webapp/src/locales/es.json index 33e57b6b..d8a072d4 100644 --- a/webapp/src/locales/es.json +++ b/webapp/src/locales/es.json @@ -1,5 +1,9 @@ { "components": { + "custommodal": { + "play": "Jugar", + "close": "Cerrar" + }, "footer": { "copyright": "Copyright 2024 ® Grupo 1A de Arquitectura del Software" }, @@ -87,7 +91,8 @@ "playAgain": "Jugar de nuevo", "back": "Volver al menú", "question": "Pregunta", - "time": "Tiempo restante:" + "time": "Tiempo restante:", + "send": "Enviar" }, "config": { "title": "Configuración", diff --git a/webapp/src/pages/Bateria/Bateria.js b/webapp/src/pages/Bateria/Bateria.js index babd88ec..0852b08e 100644 --- a/webapp/src/pages/Bateria/Bateria.js +++ b/webapp/src/pages/Bateria/Bateria.js @@ -132,8 +132,7 @@ const JuegoPreguntas = () => { const saveGame = async (endpoint, newGame) => { try { - const response = await axios.post(URL + endpoint, newGame); - console.log("Solicitud exitosa:", response.data); + await axios.post(URL + endpoint, newGame); } catch (error) { console.error("Error al guardar el juego:", error); } diff --git a/webapp/src/pages/Calculadora/Calculadora.js b/webapp/src/pages/Calculadora/Calculadora.js index 5cbbc809..dbccd2e6 100644 --- a/webapp/src/pages/Calculadora/Calculadora.js +++ b/webapp/src/pages/Calculadora/Calculadora.js @@ -100,15 +100,12 @@ const CalculadoraHumana = () => { } }; try { - const response = await axios.post(URL + '/saveGame', newGame); - console.log("Solicitud exitosa:", response.data); - + await axios.post(URL + '/saveGame', newGame); } catch (error) { console.error('Error al guardar el juego:', error); } try { - const response = await axios.post(URL + "/saveGameList", newGame); - console.log("Solicitud exitosa:", response.data); + await axios.post(URL + "/saveGameList", newGame); } catch (error) { console.error("Error al guardar el juego:", error); } @@ -173,7 +170,7 @@ const CalculadoraHumana = () => { {juegoTerminado ? ( {t('pages.humancalculator.finished')} -

Tu puntuación: {puntuacion}

+

{t("pages.humancalculator.score")} {puntuacion}

{t('pages.humancalculator.time')} {Math.floor(tiempoRestante)}

diff --git a/webapp/src/pages/Clasico/Clasico.js b/webapp/src/pages/Clasico/Clasico.js index 194c2344..48dc77da 100644 --- a/webapp/src/pages/Clasico/Clasico.js +++ b/webapp/src/pages/Clasico/Clasico.js @@ -195,8 +195,7 @@ const JuegoPreguntas = () => { }; try { - const response = await axios.post(URL + "/saveGameList", newGame); - console.log("Solicitud exitosa:", response.data); + await axios.post(URL + "/saveGameList", newGame); } catch (error) { console.error( "Error al guardar el juego en la lista de partidas:", @@ -204,8 +203,7 @@ const JuegoPreguntas = () => { ); } try { - const response = await axios.post(URL + "/saveGame", newGame); - console.log("Solicitud exitosa:", response.data); + await axios.post(URL + "/saveGame", newGame); } catch (error) { console.error("Error al guardar el juego:", error); } @@ -318,7 +316,7 @@ const JuegoPreguntas = () => {

{t("pages.classic.time")} {Math.floor(tiempoRestante)}

-

Puntuación: {puntuacion}

+

{t("pages.classic.score")} {puntuacion}

{ button.click(); const checks = screen.getAllByRole("checkbox"); - console.log(checks) checks[0].checked = true; button.click(); }); diff --git a/webapp/src/pages/History/History.js b/webapp/src/pages/History/History.js index 01c0fe83..0a8845a8 100644 --- a/webapp/src/pages/History/History.js +++ b/webapp/src/pages/History/History.js @@ -33,7 +33,6 @@ const History = () => { .then((response) => response.json()) .then((data) => { setUserData(data); - console.log(data); setLoading(false); }) .catch((error) => { diff --git a/webapp/src/pages/Perfil/Perfil.js b/webapp/src/pages/Perfil/Perfil.js index 3b163a05..4379c9c5 100644 --- a/webapp/src/pages/Perfil/Perfil.js +++ b/webapp/src/pages/Perfil/Perfil.js @@ -4,10 +4,12 @@ import Footer from "../../components/Footer/Footer.js"; import Profile from "../../components/Profile/Profile.js"; const Perfil = () => { + const username = localStorage.getItem("username"); + return ( <>