diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02aae8da..f4920fc1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,8 @@ jobs: - run: npm --prefix webapp ci - run: npm --prefix users/authservice test -- --coverage - run: npm --prefix users/userservice test -- --coverage + - run: npm --prefix questions/creationservice test -- --coverage + - run: npm --prefix questions/retrieveservice test -- --coverage - run: npm --prefix gatewayservice test -- --coverage - run: npm --prefix webapp test -- --coverage - name: Analyze with SonarCloud diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index da48ef08..3c729863 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,6 +20,8 @@ jobs: - run: npm --prefix webapp ci - run: npm --prefix users/authservice test -- --coverage - run: npm --prefix users/userservice test -- --coverage + - run: npm --prefix questions/creationservice test -- --coverage + - run: npm --prefix questions/retrieveservice test -- --coverage - run: npm --prefix gatewayservice test -- --coverage - run: npm --prefix webapp test -- --coverage - name: Analyze with SonarCloud diff --git a/docs/index.adoc b/docs/index.adoc index 468be5fd..4afb1cb2 100644 --- a/docs/index.adoc +++ b/docs/index.adoc @@ -6,7 +6,7 @@ // configure EN settings for asciidoc include::src/config.adoc[] -= image:arc42-logo.png[arc42] Template += image:arc42-logo.png[arc42] WIQ ES-2B Documentation :revnumber: 8.2 EN :revdate: January 2023 :revremark: (based upon AsciiDoc version) diff --git a/questions/creationservice/creation-service.test.js b/questions/creationservice/creation-service.test.js new file mode 100644 index 00000000..17b67e37 --- /dev/null +++ b/questions/creationservice/creation-service.test.js @@ -0,0 +1,25 @@ +const request = require('supertest'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +let mongoServer; +let app; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + process.env.MONGODB_URI = mongoUri; + app = require('./creation-service'); +}); + +afterAll(async () => { + app.close(); + await mongoServer.stop(); +}); + +describe('Retrieve Service', () => { + it('should add a new user on GET /createquestion', async () => { + + const response = await request(app).get('/createquestion'); + expect(response.status).toBe(200); + }); +}); \ No newline at end of file diff --git a/questions/retrieveservice/retrieve-service.test.js b/questions/retrieveservice/retrieve-service.test.js new file mode 100644 index 00000000..74bbccab --- /dev/null +++ b/questions/retrieveservice/retrieve-service.test.js @@ -0,0 +1,25 @@ +const request = require('supertest'); +const { MongoMemoryServer } = require('mongodb-memory-server'); + +let mongoServer; +let app; + +beforeAll(async () => { + mongoServer = await MongoMemoryServer.create(); + const mongoUri = mongoServer.getUri(); + process.env.MONGODB_URI = mongoUri; + app = require('./retrieve-service'); +}); + +afterAll(async () => { + app.close(); + await mongoServer.stop(); +}); + +describe('Retrieve Service', () => { + it('should add a new user on GET /getquestionshistory', async () => { + + const response = await request(app).get('/getquestionshistory'); + expect(response.status).toBe(200); + }); +}); \ No newline at end of file diff --git a/sonar-project.properties b/sonar-project.properties index 1807846d..f37eb5f3 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -15,7 +15,7 @@ sonar.projectName=wiq_es2b sonar.coverage.exclusions=**/*.test.js -sonar.sources=webapp/src/components,users/authservice,users/userservice,gatewayservice +sonar.sources=webapp/src/components,users/authservice,users/userservice,questions/creationservice,questions/retrieveservice,gatewayservice sonar.sourceEncoding=UTF-8 sonar.exclusions=node_modules/** sonar.javascript.lcov.reportPaths=**/coverage/lcov.info diff --git a/webapp/src/App.js b/webapp/src/App.js index 1d30bc23..c6f85bc3 100644 --- a/webapp/src/App.js +++ b/webapp/src/App.js @@ -13,34 +13,32 @@ function App() { const handleToggleView = () => { setShowLogin(!showLogin); }; - + return ( + + +
- - -
- Bienvenido a WIQ 2024 del curso de Arquitectura del Software - {showLogin ? : } - - {showLogin ? ( - - ¿No tienes una cuenta? Regístrate aquí. - - ) : ( - - ¿Ya tienes cuenta? Inicia sesión aquí. - - )} - - -
-
+ {showLogin ? : } + + {showLogin ? ( + + ¿No tienes una cuenta? Regístrate aquí. + + ) : ( + + ¿Ya tienes cuenta? Inicia sesión aquí. + + )} + +
+
); } diff --git a/webapp/src/Timer.css b/webapp/src/Timer.css index 2fe296ea..ec1654b9 100644 --- a/webapp/src/Timer.css +++ b/webapp/src/Timer.css @@ -1,7 +1,7 @@ .Timer { font-family: sans-serif; text-align: center; - height: 50vh; + height: 30vh; display: flex; justify-content: center; align-items: center; @@ -9,12 +9,12 @@ .Timer .container { position: relative; - transform: scale(200%); + transform: scale(150%); } .Timer .text { position: absolute; - color: #007bff; + color: #23384b; top: 50%; left: 50%; transform: translate(-50%, -50%); @@ -31,7 +31,7 @@ .Timer .container svg circle { fill: transparent; - stroke: #415262; + stroke: #23384b; stroke-width: 4; transform: translate(5px, 5px); transition: all 2s; @@ -40,7 +40,7 @@ .Timer .container svg circle:nth-child(2) { fill: transparent; - stroke: #007bff; + stroke: #62bbff; stroke-width: 6; stroke-dasharray: 440; /* stroke-dashoffset: 0; */ @@ -60,12 +60,12 @@ content: ""; width: 20px; height: 20px; - background-color: #007bff; + background-color: #62bbff; z-index: 1000; position: absolute; border-radius: 10px; top: -2px; transform: translate(-10px); - box-shadow: 0 0 20px 4px #007bff; + box-shadow: 0 0 20px 4px #62bbff; } \ No newline at end of file diff --git a/webapp/src/components/Footer.css b/webapp/src/components/Footer.css new file mode 100644 index 00000000..c64e75ff --- /dev/null +++ b/webapp/src/components/Footer.css @@ -0,0 +1,20 @@ +.footer { + position: absolute; + width: 100%; + background-color: #f5f5f5; + padding: 0.5rem; + text-align: center; + margin-top: 90vh; + height: auto; + bottom: 0; +} + +.footer a { + color: #a5b8d4; + text-decoration: none; + font-weight: 600; +} + +.footer a:hover { + text-decoration: underline; +} \ No newline at end of file diff --git a/webapp/src/components/Footer.js b/webapp/src/components/Footer.js new file mode 100644 index 00000000..904473f0 --- /dev/null +++ b/webapp/src/components/Footer.js @@ -0,0 +1,17 @@ +import React from 'react'; +import './Footer.css'; // Importa el archivo de estilos CSS +import { AppBar, Toolbar, Typography } from '@mui/material'; + +const Footer = () => { + return ( + + + + © {new Date().getFullYear()} Hecho con ❤️ por Coral, Carlos, Pablo y Raymond. ASW - Curso 2023-24 + + + + ); +}; + +export default Footer; diff --git a/webapp/src/components/Game.css b/webapp/src/components/Game.css index 8bb1f25b..46f5547b 100644 --- a/webapp/src/components/Game.css +++ b/webapp/src/components/Game.css @@ -1,28 +1,64 @@ -button[title="sigPreg"] { - margin: 1em; - margin-left: 2em; - background-color: rgba(31, 60, 134, 0.764); -} - -button[title="contador"]:disabled{ - margin: 1em; - background-color: rgba(31, 60, 134, 0.764); +button[title="contador"]:disabled { + margin: 0.5em; + background-color: #0155B7; color: white; + padding: 0.5rem; } -button[title="btnsPreg"]{ +button[title="btnsPreg"] { margin: 0.5em; padding-top: 0.2em; padding-bottom: 0.2em; - background-color: rgba(29, 86, 109, 0.764); + background-color: #4c8dbf; + font-weight: 700; +} + +button[title="puntuacion"]:disabled { + margin: 1em; + background-color: #0155B7; + color: white; + padding-top: 0.4em; + padding-bottom: 0.2em; + font-size: 1.5em; +} + + +button[title="volver"] { + margin-top: 0.5em; } -button[title="puntuacion"]:disabled{ + +button[title="volver"]:disabled { margin: 1em; - background-color: rgba(31, 60, 134, 0.764); + background-color: #0155B7; color: white; padding-top: 0.4em; padding-bottom: 0.2em; font-size: 1.5em; } +.game-question h2 { + font-size: 1.5rem; +} + +.button-container { + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} + + +@media (max-width: 600px) { + .button-container { + flex-direction: column; + align-items: center; + gap: 0; + margin: 0; + } + + .button-container button { + font-size: 12px; + padding: 5px 10px; + } +} \ No newline at end of file diff --git a/webapp/src/components/Game.js b/webapp/src/components/Game.js index ff273c5a..20875ec6 100644 --- a/webapp/src/components/Game.js +++ b/webapp/src/components/Game.js @@ -1,22 +1,23 @@ import React, { useState, useEffect } from 'react'; import axios from 'axios'; import { Container, Typography, Button, Paper, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions} from '@mui/material'; - -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import './Game.css'; - import '../index.css'; - import '../Timer.css'; -const colorPreguntas= 'rgba(51, 139, 173, 0.764)'; +const colorPreguntas= '#4c8dbf'; const colorOnMousePreguntas= 'rgba(28, 84, 106, 0.764)'; const Game = () => { const navigate = useNavigate(); const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + // Configuración del juego + const location = useLocation(); + const { gameConfig } = location.state; + const [questionObject, setQuestionObject] = useState(''); const [correctOption, setCorrectOption] = useState(''); const [answerOptions, setAnswerOptions] = useState([]); @@ -24,9 +25,12 @@ const Game = () => { const [questionCounter, setQuestionCounter] = useState(0); const [incorrectCounter, setIncorrectCounter] = useState(0); - - const [numberOfQuestions] = useState(6); - const [questionsToAnswer, setQuestionsToAnswer] = useState(6); + + const [numberOfQuestions] = useState(gameConfig.numQuestions); + const [questionsToAnswer, setQuestionsToAnswer] = useState(gameConfig.numQuestions); + + + const [isFinished, setFinished] = useState(false); // Porcentaje de aciertos @@ -43,8 +47,8 @@ const Game = () => { - // Temporizador desde 20 segundos - const [time, setTime] = useState(20); + // Temporizador + const [time, setTime] = useState(gameConfig.timePerQuestion); const [isTimedOut, setTimedOut] = useState(false); @@ -110,7 +114,7 @@ const Game = () => { // Calcular el porcentaje de tiempo transcurrido para el círculo del temporizador - const percentageTime = ((20 - time) / 20) * 100; + const percentageTime = ((gameConfig.timePerQuestion - time) / gameConfig.timePerQuestion) * 100; // Detener el temporizador @@ -124,7 +128,7 @@ const Game = () => { // Activar el temporizador const restartTimer = () => { - setTime(20); // Reiniciar el tiempo a 20 segundos + setTime(gameConfig.timePerQuestion); // Reiniciar el tiempo setIsTimerActive(true); setTimedOut(false); }; @@ -202,7 +206,7 @@ const Game = () => { console.error('Error:', error); } - // Poner temporizador a 20 segundos + // Poner temporizador a tiempo inicial restartTimer(); @@ -379,13 +383,31 @@ const getQuestions = () => { - {!isFinished && ( - + Saber y Ganar Juego +
+ {!isFinished && ( + + )} + {!isFinished && ( + + )} + {!isFinished && ( + + )} +
+ + {!isFinished && (
@@ -395,10 +417,7 @@ const getQuestions = () => {
{time}
-
+ { )} - - Pregunta {questionCounter}: {questionObject} + +

Pregunta {questionCounter}: {questionObject}

{answerOptions.map((option, index) => ( @@ -437,21 +456,7 @@ const getQuestions = () => { )} - {!isFinished && ( - - )} - {!isFinished && ( - - )} - {!isFinished && ( - - )} + @@ -476,7 +481,7 @@ const getQuestions = () => {
-
@@ -484,7 +489,7 @@ const getQuestions = () => { {!isGameFinished() && !isFinished &&(
-
)} diff --git a/webapp/src/components/MainPage.css b/webapp/src/components/MainPage.css index dee8e995..58569311 100644 --- a/webapp/src/components/MainPage.css +++ b/webapp/src/components/MainPage.css @@ -3,15 +3,16 @@ div[title="main"] { grid-template-columns: 1fr; } -div[title="main-title"] > h1 { - margin: 1.5rem; - color: rgb(24, 46, 63); +div[title="main-title"]>h1 { + margin-top: 1.5rem; + margin-bottom: -1rem; + color: #182e3f; font-size: 3rem; font-weight: 400; } -div[title="main-title"] > h2 { - margin: -2rem; +div[title="main-title"]>h2 { + /* margin-bottom: 2rem; */ color: #4c8dbf; font-size: 4rem; font-weight: bold; @@ -23,6 +24,7 @@ div[title="main-title"] > h2 { from { transform: translateX(0px); } + to { transform: translateX(20px); } @@ -30,7 +32,7 @@ div[title="main-title"] > h2 { div[title="main"]>button:hover { - background-color: rgb(189, 216, 255); + background-color: #bdd8ff; color: #007dfe; font-weight: 600; width: auto; @@ -38,14 +40,13 @@ div[title="main"]>button:hover { height: 3rem; } -div[title="main"]>button{ +div[title="main"]>button { background-color: #0155B7; color: white; font-weight: 600; width: auto; font-size: 1em; height: 3rem; - margin: 1rem; } @@ -53,11 +54,11 @@ div[title="main"]>button{ .img-container { text-align: center; - margin-bottom: 20px; /* Ajusta el margen inferior según sea necesario */ + margin-bottom: 2rem; } .img-container img { - width: 75%; + width: 70rem; height: auto; animation: slide 3s ease infinite alternate; } @@ -70,3 +71,21 @@ div[title="main"]>button{ +.dialogContainer { + color: #4c8dbf; +} + +.dialogTitle h2 { + font-weight: 700; + font-size: 2rem; + color: #0155B7; +} + + +.dialogImage { + text-align: center; +} + +.dialogImage img { + width: 18rem; +} \ No newline at end of file diff --git a/webapp/src/components/MainPage.js b/webapp/src/components/MainPage.js index 7d149e6d..d444c689 100644 --- a/webapp/src/components/MainPage.js +++ b/webapp/src/components/MainPage.js @@ -1,35 +1,72 @@ -import React from 'react'; -import { Container, Typography, Button, Grid } from '@mui/material'; +// MainPage.js +import React, { createContext, useContext, useState } from 'react'; +import { Container, Typography, Button, Grid, Dialog, DialogTitle, DialogContent, TextField, DialogActions } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import './MainPage.css'; import Navbar from './Navbar'; +import Footer from './Footer'; + +// Definición del contexto para la configuración del juego +const ConfigContext = createContext(); + +export const useConfig = () => useContext(ConfigContext); const MainPage = () => { const navigate = useNavigate(); + // Configuración de la partida + const [open, setOpen] = useState(false); + const [numQuestions, setNumQuestions] = useState(5); + const [timePerQuestion, setTimePerQuestion] = useState(10); + + const handleNumQuestionsChange = (event) => { + setNumQuestions(event.target.value); + }; + + const handleTimePerQuestionChange = (event) => { + setTimePerQuestion(event.target.value); + }; + + const handleOpenDialog = () => { + setOpen(true); + }; + + const handleCloseDialog = () => { + setOpen(false); + }; + + const handleInputChange = (event) => { + event.preventDefault(); + }; + const handleShowGame = () => { let path = '/Game'; - navigate(path); - }; - const handleShowHistoricalData = () => { - let path = '/HistoricalData'; - navigate(path); + // Configuración del juego + const gameConfig = { + numQuestions: numQuestions, + timePerQuestion: timePerQuestion + }; + + navigate(path, { state: { gameConfig } }); }; - const handleShowHistoricalUserData = () => { - let path = '/HistoricalUserData'; + const handleRanking = () => { + let path = '/ScoreBoard'; navigate(path); }; - const handleShowRegisteredUsers = () => { - let path = '/RegisteredUsers'; - navigate(path); + // Valor del contexto para la configuración del juego + const configValue = { + numQuestions, + timePerQuestion, + updateNumQuestions: setNumQuestions, + updateTimePerQuestion: setTimePerQuestion, }; return ( - <> +
@@ -41,8 +78,8 @@ const MainPage = () => {
- - + +
@@ -54,20 +91,63 @@ const MainPage = () => { - - - -
+
- + + +
+ +

Configuración del juego

+
+ +
+ Descripción de la imagen +
+ Ingrese el número de preguntas: + + + Ingrese el tiempo por pregunta (segundos): + +
+ + + +
+
+ +