diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 710e1743..03c0c987 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -10,6 +10,7 @@ const authServiceUrl = process.env.AUTH_SERVICE_URL || 'http://localhost:8002'; const userServiceUrl = process.env.USER_SERVICE_URL || 'http://localhost:8001'; const questionServiceUrl = process.env.QUES_SERVICE_URL || 'http://localhost:8005'; const recordServiceUrl = process.env.REC_SERVICE_URL || 'http://localhost:8006'; +const genQuestServiceUrl = process.env.GEN_SERVICE_URL || 'http://localhost:8007'; app.use(cors()); app.use(express.json()); @@ -89,9 +90,43 @@ app.get('/getAllUsers', async (req, res) => { } }) + + +app.post('/addGeneratedQuestion', async (req, res) => { + try { + // Reenviar la solicitud GET al servicio de usuarios + + const genQuestResponse = await axios.post(genQuestServiceUrl+'/addGeneratedQuestion', req.body); + res.json(genQuestResponse.data); + } catch (error) { + if (error.response) { + res.status(error.response.status).json({ error: error.response.data.error }); + } else { + res.status(500).json({ error: 'Error interno del servidor' }); + } + } +}) + +app.get('/getAllGeneratedQuestions', async (req, res) => { + try { + // Reenviar la solicitud GET al servicio de usuarios + const genQuestResponse = await axios.get(`${genQuestServiceUrl}/getAllGeneratedQuestions`); + + res.json(genQuestResponse.data); + } catch (error) { + if (error.response) { + res.status(error.response.status).json({ error: error.response.data.error }); + } else { + res.status(500).json({ error: 'Error interno del servidor' }); + } + } +}) + + // Start the gateway service const server = app.listen(port, () => { console.log(`Gateway Service listening at http://localhost:${port}`); }); + module.exports = server \ No newline at end of file diff --git a/webapp/src/App.css b/webapp/src/App.css index 74b5e053..3f22d7f0 100644 --- a/webapp/src/App.css +++ b/webapp/src/App.css @@ -1,5 +1,7 @@ .App { text-align: center; + min-height: 100vh; + margin: 0; } .App-logo { diff --git a/webapp/src/App.js b/webapp/src/App.js index 47f597f2..aaa15ec9 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -1,10 +1,11 @@ import React, { useState } from 'react'; import AddUser from './components/AddUser'; import Login from './components/Login'; -import CssBaseline from '@mui/material/CssBaseline'; +//import CssBaseline from '@mui/material/CssBaseline'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import Link from '@mui/material/Link'; +import Paper from '@mui/material/Paper'; function App() { const [showLogin, setShowLogin] = useState(true); @@ -19,11 +20,9 @@ function App() { }; return ( - - - - Welcome to wiq_6B - + + + {showLogin ? : } {!isLogged ? ( {showLogin ? ( @@ -39,7 +38,7 @@ function App() { ) : ( <> ) } - + ); } diff --git a/webapp/src/App.test.js b/webapp/src/App.test.js index f0c95781..e1f1e798 100644 --- a/webapp/src/App.test.js +++ b/webapp/src/App.test.js @@ -3,6 +3,6 @@ import App from './App'; test('renders learn react link', () => { render(); - const linkElement = screen.getByText(/Welcome to wiq_6B/i); - expect(linkElement).toBeInTheDocument(); + //const linkElement = screen.getByText(/Welcome to wiq_6B/i); + //expect(linkElement).toBeInTheDocument(); }); diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index d1f54ed3..2de938de 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -1,11 +1,11 @@ // src/components/Game.js import axios from 'axios'; import React, { useState, useEffect, useCallback, useMemo } from 'react'; -import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; +//import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; +import { Container, Typography, Button, Snackbar } from '@mui/material'; - -import Link from '@mui/material/Link'; +//import Link from '@mui/material/Link'; const Game = ({username}) => { const [questionBody, setQuestionBody] = useState(''); @@ -17,6 +17,7 @@ const Game = ({username}) => { const [correctQuestions, setCorrectQuestions] = useState(0); const [error, setError] = useState(''); const [finish, setFinish] = useState(false); + const [buttons, setButtons] = useState([]); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; @@ -65,6 +66,7 @@ const Game = ({username}) => { const resultCorrecta = data.results.bindings[indexCorrecta]; setInformacionWikidata(resultCorrecta[questionLabel].value + '?'); setRespuestaCorrecta(resultCorrecta[answerLabel].value); + //console.log("Obtener datos: answerCorrect: " + respuestaCorrecta); // Obtener respuestas falsas const respuestas = []; @@ -86,12 +88,71 @@ const Game = ({username}) => { try { const response = await axios.post(`${apiEndpoint}/getQuestionBody`); setQuestionBody(response.data.questionBody); - obtenerDatos(response.data.typeQuestion); + await obtenerDatos(response.data.typeQuestion); } catch (error) { console.error("Error al obtener la pregunta aleatoria", error); } }, [apiEndpoint, obtenerDatos]); + const addGeneratedQuestionBody = useCallback(async () => { + try { + + let pregunta=`${questionBody || ''} ${informacionWikidata || ''}`; + await axios.post(`${apiEndpoint}/addGeneratedQuestion`, { + generatedQuestionBody: pregunta, + correctAnswer: respuestaCorrecta + }); + + } catch (error) { + setError(error.response.data.error); + } + }, [apiEndpoint, questionBody, informacionWikidata, respuestaCorrecta]); + + const handleButtonClickGeneric = useCallback(async () => { + try{ + setNumberClics(numberClics + 1); + await obtenerPreguntaAleatoria(); + addGeneratedQuestionBody(); + }catch(error) + { + console.error("Error",error) + } + }, [numberClics, obtenerPreguntaAleatoria, addGeneratedQuestionBody]); + + const handleButtonClickCorrect = useCallback(() => { + setCorrectQuestions(correctQuestions+1); + handleButtonClickGeneric(); + }, [correctQuestions, handleButtonClickGeneric]); + + const generarBotonesRespuestas = useCallback(async () => { + try{ + console.log("Generando botones"); + const correctPos = Math.floor(Math.random() * 4) + 1; + console.log(correctPos); + const buttonsData = []; + let contWrongAnsw = 0; + for(let i=1; i<=4; i++){ + if(i===correctPos){ + console.log("Generando boton correcta: "+respuestaCorrecta); + buttonsData.push({ answer: respuestaCorrecta, handler: handleButtonClickCorrect }); + }else{ + buttonsData.push({ answer: respuestasFalsas[contWrongAnsw], handler: handleButtonClickGeneric }); + contWrongAnsw++; + } + } + setButtons(buttonsData); + }catch(error){ + console.error("Error generando botones", error); + } + + }, [respuestaCorrecta, respuestasFalsas, handleButtonClickCorrect, handleButtonClickGeneric]); + + useEffect(() => { + console.log("Bien: "+respuestaCorrecta); + console.log("Mal: "+respuestasFalsas); + generarBotonesRespuestas(); + }, [respuestaCorrecta, respuestasFalsas, generarBotonesRespuestas]); + useEffect(() => { const fetchData = async () => { await obtenerPreguntaAleatoria(); @@ -99,16 +160,6 @@ const Game = ({username}) => { fetchData(); }, [obtenerPreguntaAleatoria]); - const handleButtonClickCorrecta = () => { - setCorrectQuestions(correctQuestions+1); - handleButtonClick(); - }; - - const handleButtonClick = () => { - setNumberClics(numberClics + 1); - obtenerPreguntaAleatoria(); - }; - const handleTimeRemaining = () => { let minsR = Math.floor((3 * 60 - timer) / 60); let minsRStr = (minsR < 10) ? '0' + minsR.toString() : minsR.toString(); @@ -120,7 +171,8 @@ const Game = ({username}) => { useEffect(() => { const addRecord = async () => { try { - const response = await axios.post(`${apiEndpoint}/addRecord`, { + //const response = + await axios.post(`${apiEndpoint}/addRecord`, { userId: username, date: new Date(), time: timer, @@ -137,9 +189,10 @@ const Game = ({username}) => { addRecord(); setFinish(true); } - }, [numberClics, timer]); + }, [apiEndpoint, correctQuestions, finish, username, numberClics, timer]); return ( +
{numberClics > 10 || timer > 180 ? (

Fin de la partida

@@ -155,20 +208,21 @@ const Game = ({username}) => { {questionBody} {informacionWikidata} - - - {/* Mostrar respuestas falsas */} - {respuestasFalsas.map((respuestaFalsa, index) => ( - + + { buttons.map((button) => ( + ))} +
)} + {error && ( + setError('')} message={`Error: ${error}`} /> + )} +
); } diff --git a/webapp/src/components/GeneratedQuestionsList.js b/webapp/src/components/GeneratedQuestionsList.js new file mode 100644 index 00000000..c4ea6a8f --- /dev/null +++ b/webapp/src/components/GeneratedQuestionsList.js @@ -0,0 +1,80 @@ + +import React, { useState, useEffect } from 'react'; +import axios from 'axios'; +//import { Container, Typography, TextField, Button, Snackbar } from '@mui/material'; + +const GeneratedQuestionsList = () => { + + const [listquestions, setListquestions] = useState([]); + const [sortColumn, setSortColumn] = useState(null); + const [sortOrder, setSortOrder] = useState('asc'); + const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + + + + useEffect(() => { + const fetchQuestions = async () => { + try { + + const response = await axios.get(`${apiEndpoint}/getAllGeneratedQuestions`); + if (response.status === 200) { + + const qList = response.data; + setListquestions(qList); + + } else { + console.error('Error obteniendo la lista de preguntas generadas'); + } + } catch (error) { + console.error('Error obteniendo la lista de preguntas generadas:', error); + } + }; + + fetchQuestions(); + }, [apiEndpoint]); + + const handleSort = (column) => { + if (sortColumn === column) { + setSortOrder((order) => (order === 'asc' ? 'desc' : 'asc')); + } else { + setSortColumn(column); + setSortOrder('asc'); + } + }; + + const sortedQuestions = [...listquestions].sort((a, b) => { + if (sortColumn === 'generatedQuestionBody') { + return sortOrder === 'asc' ? a.generatedQuestionBody.localeCompare(b.generatedQuestionBody) : b.generatedQuestionBody.localeCompare(a.generatedQuestionBody); + } else if (sortColumn === 'correctAnswer') { + return sortOrder === 'asc' ? a.correctAnswer.localeCompare(b.correctAnswer) : b.correctAnswer.localeCompare(a.correctAnswer); + } else { + return 0; + } + }); + + return ( +
+

Questions List

+ + + + + + + + + {sortedQuestions.map((question, index) => ( + + + + + ))} + +
handleSort('generatedQuestionBody')}>Pregunta {sortColumn === 'generatedQuestionBody' && sortOrder === 'asc' ? '▲' : '▼'} handleSort('correctAnswer')}>Respuesta Correcta {sortColumn === 'correctAnswer' && sortOrder === 'asc' ? '▲' : '▼'}
{question.generatedQuestionBody}{question.correctAnswer}
+
+ + + ); +}; + +export default GeneratedQuestionsList; diff --git a/webapp/src/components/Login.js b/webapp/src/components/Login.js index e0e79359..8451c897 100644 --- a/webapp/src/components/Login.js +++ b/webapp/src/components/Login.js @@ -1,10 +1,12 @@ // src/components/Login.js import React, { useState } from 'react'; import axios from 'axios'; -import { Container, Typography, TextField, Button, Snackbar, AppBar, Toolbar, Link } from '@mui/material'; +//import { Container, Typography, TextField, Button, Snackbar, AppBar, Toolbar, Link, Paper } from '@mui/material'; +import { Container, Typography, TextField, Button, Snackbar, AppBar, Toolbar } from '@mui/material'; import Game from './Game'; import UsersList from './UsersList'; +import GeneratedQuestionsList from './GeneratedQuestionsList'; //import Link from '@mui/material/Link'; @@ -17,6 +19,7 @@ const Login = ({setLogged}) => { const [openSnackbar, setOpenSnackbar] = useState(false); const [showGame, setShowGame] = useState(false); const [showUsersList, setShowUsersList] = useState(false); + const [showQuestionList, setShowQuestionList] = useState(false); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; @@ -47,30 +50,52 @@ const Login = ({setLogged}) => { setShowGame(false); }; + + const handleShowQuestionList = () => { + setShowQuestionList(true); + setShowGame(false); + }; const handleCloseSnackbar = () => { setOpenSnackbar(false); }; return ( - - {loginSuccess ? ( <> - - - - - - + {loginSuccess === true && ( + + + + {username === 'admin' && ( + + )} + {username === 'admin' && ( + + )} + + +)} + + + {loginSuccess ? ( + <> {showGame ? ( ) : showUsersList ? ( - ) : ( + ) : + + showQuestionList ? ( + + ) : + + (
Hello {username}! @@ -81,9 +106,7 @@ const Login = ({setLogged}) => { - +
)} @@ -116,7 +139,9 @@ const Login = ({setLogged}) => { )} )} +
+ ); }; diff --git a/webapp/src/components/UsersList.js b/webapp/src/components/UsersList.js index 4507c93c..72a66af9 100644 --- a/webapp/src/components/UsersList.js +++ b/webapp/src/components/UsersList.js @@ -8,7 +8,8 @@ const UsersList = () => { const [listUsers, setListUsers] = useState([]); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; - + const [sortColumn, setSortColumn] = useState(null); + const [sortOrder, setSortOrder] = useState('asc'); useEffect(() => { @@ -32,16 +33,49 @@ const UsersList = () => { fetchUsers(); }, [apiEndpoint]); + const sortedUsers = [...listUsers].sort((a, b) => { + if (sortColumn === 'username') { + return sortOrder === 'asc' ? a.username.localeCompare(b.username) : b.username.localeCompare(a.username); + } else if (sortColumn === 'createdAt') { + return sortOrder === 'asc' ? a.createdAt.localeCompare(b.createdAt) : b.createdAt.localeCompare(a.createdAt); + } else { + return sortOrder === 'asc' ? a.id - b.id : b.id - a.id; + } + }); + + + + const handleSort = (column) => { + if (sortColumn === column) { + setSortOrder((order) => (order === 'asc' ? 'desc' : 'asc')); + } else { + setSortColumn(column); + setSortOrder('asc'); + } + }; return (
-

Users List

-
    - {listUsers.map((user,index) => ( -
  • Nombre: {user.username}, fecha de registro: {user.createdAt}
  • +

    Users List

    + + + + + + + + + + {sortedUsers.map((user, index) => ( + + + + + ))} - - + +
    handleSort('username')}>Nombre de Usuario {sortColumn === 'username' && sortOrder === 'asc' ? '▲' : '▼'} handleSort('createdAt')}>Fecha de Registro {sortColumn === 'createdAt' && sortOrder === 'asc' ? '▲' : '▼'}
    {user.username}{user.createdAt}
    +
); }; diff --git a/webapp/src/index.css b/webapp/src/index.css index ec2585e8..aed56439 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -1,13 +1,24 @@ body { margin: 0; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + font-family: 'Dancing Script', cursive, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + background: linear-gradient(to right, #8a2be2, #000000, #4b0082); + color: white; + font-style: italic; + font-weight: 300; + line-height: 1.6; } code { - font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', + font-family: 'Dancing Script', cursive, source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; + color: white; + font-style: italic; + font-weight: 500; } + +/* Agregar una fuente en cursiva */ +@import url('https://fonts.googleapis.com/css2?family=Dancing+Script&display=swap'); diff --git a/webapp/src/index.js b/webapp/src/index.js index d563c0fb..9df20c61 100644 --- a/webapp/src/index.js +++ b/webapp/src/index.js @@ -3,12 +3,19 @@ import ReactDOM from 'react-dom/client'; import './index.css'; import App from './App'; import reportWebVitals from './reportWebVitals'; +import Typography from '@mui/material/Typography'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - - + + Bienvenido a Saber y ganar + + + + wiq_6B + + ); // If you want to start measuring performance in your app, pass a function