Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Añadido el tiempo de la partida y paramertrizado el tiempo por pregunta #139

Merged
merged 7 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions webapp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"react": "^18.2.0",
"react-bootstrap": "^2.10.1",
"react-dom": "^18.2.0",
"react-icons": "^5.0.1",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"web-vitals": "^3.5.1"
Expand Down
Binary file modified webapp/public/favicon.ico
Binary file not shown.
Binary file added webapp/public/faviconReact.ico
Binary file not shown.
Binary file added webapp/public/logoGitHub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 18 additions & 5 deletions webapp/src/App.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useState,useEffect} from 'react';
import Navbar from './components/navbar/NavBar';
import AddUser from './components/adduser/AddUser';
import Login from './components/login/Login';
Expand All @@ -8,20 +8,33 @@ import StartButton from './components/startbutton/StartButton';
import Game from './components/game/Game';
import Home from './components/home/Home';
import Footer from './components/footer/Footer';
import { ChakraProvider } from '@chakra-ui/react';

import { ChakraProvider } from "@chakra-ui/react";
import AuthenticatedLayout from './components/authenticationLayout';
import GuestLayout from './components/GuestLayout';
import Logout from './components/logout/Logout';
import History from './components/history/History';
import {BasicGameMode } from './components/game/gameModes/basicGameMode';
import {GameProvider} from './components/game/GameContext';
import PrincipalView from './components/principalView/PrincipalView';

const App = () => {

const [darkMode, setDarkMode] = useState(false);

useEffect(() => {
if (darkMode) {
document.body.classList.add('dark-mode');
console.log('dark mode');
} else {
document.body.classList.remove('dark-mode');
console.log('light mode');
}
}, [darkMode]);

return (
<AuthProvider>
<Router>
<Navbar />
<Navbar setDarkMode={setDarkMode}/>
<GameProvider gameMode={new BasicGameMode()}>

<Routes>
Expand All @@ -37,7 +50,7 @@ const App = () => {
} />
<Route path="/game" element={
<AuthenticatedLayout>
<ChakraProvider><Game /> </ChakraProvider>
<ChakraProvider><Game darkMode={darkMode}/> </ChakraProvider>
</AuthenticatedLayout>
} />
<Route path="/history" element={
Expand Down
6 changes: 6 additions & 0 deletions webapp/src/components/footer/Footer.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@
text-decoration: underline; /* Subraya enlaces al pasar el ratón */
}

.sec-footer {
display: flex;
justify-content: space-around;
align-items: center;
}

12 changes: 8 additions & 4 deletions webapp/src/components/footer/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import './Footer.css'; // Asegúrate de importar tu archivo de estilos si es nec

const Footer = () => (
<footer className="footer">
<div className="sec-footer">
<p>Trabajo de Arquitectura del Software</p>
<p>
</div>
<div className="sec-footer">
<img src="/logoGitHub.png" alt="Github" style={{ width: '20px', marginRight: '5px' }} />
<a href="https://github.com/Arquisoft/wiq_es04c" target="_blank" rel="noopener noreferrer">
Github del Proyecto
</a>
</p>
<p>
<a href="https://ingenieriainformatica.uniovi.es" target="_blank"rel="noopener noreferrer">Escuela de Ingeniería Informática</a></p>
</div>
<div className="sec-footer">
<a href="https://ingenieriainformatica.uniovi.es" target="_blank"rel="noopener noreferrer">Escuela de Ingeniería Informática</a>
</div>
</footer>
);

Expand Down
6 changes: 4 additions & 2 deletions webapp/src/components/game/AnswerButton.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {Button} from '@chakra-ui/react'

export function AnswerButton({text, colorFondo, onClick}){
export function AnswerButton({darkMode,text, colorFondo, onClick}){

let textColor = darkMode.darkMode? "#FCFAF0" : "#08313A";

return(
<Button
bg={colorFondo}
color="#FCFAF0"
color={textColor}
display="flex"
fontSize="1.3em"
borderRadius="15px"
Expand Down
7 changes: 4 additions & 3 deletions webapp/src/components/game/AnswersBlock.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { Box } from "@chakra-ui/react";
import React, { useEffect, useState } from 'react';
import { AnswerButton } from './AnswerButton.jsx';
//onAnswerSelect es la funcion de QuestionArea que se ejecuta al hacer click en boton
export function AnswersBlock({ respuestas, correcta ,onAnswerSelect,isGameEnded}){
export function AnswersBlock({ respuestas, correcta ,onAnswerSelect,isGameEnded, darkMode}){

const [respuestasAleatorizadas, setRespuestasAleatorizadas] = useState([]);

let respuestasCopy = respuestas;

//Colores de los botones para que tengan orden random
const colorsArray = ["#FFD743","#D773A2","#07BB9C","#A06AB4"];
console.log("En asnblock "+darkMode.darkMode);
const colorsArray= darkMode.darkMode? ["#FFD743","#D773A2","#07BB9C","#A06AB4"] : ["#E3C7E0","#BAE5C0","#84C7F2","#E8F7FF"];
//Baraja con algoritmo de Fisher-Yates los colores
for (let i = colorsArray.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
Expand Down Expand Up @@ -40,7 +41,7 @@ export function AnswersBlock({ respuestas, correcta ,onAnswerSelect,isGameEnded}
return (
<Box display="grid" flex="1" gridTemplateColumns="repeat(2,1fr)" gridColumnGap="2em" padding="4em" alignItems="center">
{respuestasAleatorizadas.map((respuesta, index) => (
<AnswerButton key={index} text={respuesta} colorFondo={colorsArray[index]} onClick={() => handleButtonClick(respuesta)} />
<AnswerButton darkMode={darkMode} key={index} text={respuesta} colorFondo={colorsArray[index]} onClick={() => handleButtonClick(respuesta)} />
))}
</Box>
);
Expand Down
6 changes: 4 additions & 2 deletions webapp/src/components/game/EnunciadoBlock.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import {Center } from "@chakra-ui/react";


export function EnunciadoBlock({pregunta}){
export function EnunciadoBlock({darkMode, pregunta}){

let textColor = darkMode.darkMode? "#FCFAF0" : "#08313A";

return(
<Center fontSize="1.5em"
color="#FCFAF0" fontWeight="bolder"flex="2">
color={textColor} fontWeight="bolder"flex="2">
{pregunta}
</Center>
)
Expand Down
17 changes: 12 additions & 5 deletions webapp/src/components/game/Game.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { QuestionArea } from './QuestionArea';
import { useEffect, useState,useContext } from 'react';
import { useEffect, useState,useContext, useRef } from 'react';
import { CircularProgress } from "@mui/material";
import {GameContext} from './GameContext';
import {useNavigate} from 'react-router-dom';
import { Box, AlertDialog, AlertDialogBody, AlertDialogFooter, AlertDialogHeader, AlertDialogContent, AlertDialogOverlay, Button,Center } from "@chakra-ui/react";

/*
recibe el obj gameMode que contieene las preguntas para ese modo de juego */
function Game() {
function Game(darkMode) {
const apiEndpoint = process.env.REACT_APP_API_URI ||'http://localhost:8000';

const { startGame, questions, isLoading } = useContext(GameContext);
Expand All @@ -18,6 +18,7 @@ function Game() {
const [incorrectAnswers, setIncorrectAnswers] = useState(0);
const [finished, setFinished] = useState(false);
const navigate = useNavigate();
const timeToAnswer = 20000;//Aquí podemos definir el tiempo para responder


useEffect(() => {
Expand Down Expand Up @@ -48,16 +49,22 @@ function Game() {
setIsOpen(false);
navigate('/home');
}
//Colores chakra dark - light
console.log("En game"+darkMode.darkMode);
let backgroundColorFirst= darkMode.darkMode? '#08313A' : '#FFFFF5';
let backgroundColorSecond= darkMode.darkMode? '#107869' : '#FDF4E3';
//#08313A, #107869

return (
<Box minH="100vh" minW="100vw"
bgGradient="linear(to-t, #08313A, #107869)"
bgGradient={`linear(to-t, ${backgroundColorFirst}, ${backgroundColorSecond})`}
display="flex" justifyContent="center" alignItems="center">
{isLoading ? (
<CircularProgress color="inherit" />

) : (
<QuestionArea data-testid="question-area" questions={questions} setTotalCorrectAnswers={setCorrectAnswers}
setTotalIncorrectAnswers={setIncorrectAnswers} setFinished={setFinished}/>
<QuestionArea darkMode={darkMode} data-testid="question-area" questions={questions} setTotalCorrectAnswers={setCorrectAnswers}
setTotalIncorrectAnswers={setIncorrectAnswers} setFinished={setFinished} timeToAnswer={timeToAnswer}/>
)}
<AlertDialog isOpen={isOpen} onClose={onClose}>
<AlertDialogOverlay>
Expand Down
18 changes: 9 additions & 9 deletions webapp/src/components/game/QuestionArea.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { useEffect, useState,useRef } from 'react';
import { Box } from "@chakra-ui/react";
import { AnswersBlock } from './AnswersBlock.jsx';
import { EnunciadoBlock } from './EnunciadoBlock.jsx';
import { Timer } from './Timer';
import { Timer } from './timers/Timer';
import { GameTimer } from './timers/GameTimer.jsx';

/*
*maneja la logica general del juego reincia el contador del timepo salta de pregunas etc,
cuando el juego termina actualiza las respuestas correctas e incorrectas y se las apsas a game con prop Drilling */

export function QuestionArea({questions,setTotalCorrectAnswers, setTotalIncorrectAnswers,setFinished}){
export function QuestionArea({darkMode, questions,setTotalCorrectAnswers, setTotalIncorrectAnswers,setFinished, timeToAnswer=30000}){
const [questionIndex, setQuestionIndex] = useState(0); // Nuevo estado para el índice de la pregunta
// Estado para almacenar los datos de la pregunta
const [questionData, setQuestionData] = useState(null); // Estado para almacenar los datosS de la pregunta
Expand All @@ -19,6 +20,8 @@ export function QuestionArea({questions,setTotalCorrectAnswers, setTotalIncorrec
const [open, setOpen] = useState(false); // Nuevo estado para controlar si el diálogo está abierto o cerrado
const [correctAnswers, setCorrectAnswers] = useState(0); // Nuevo estado para llevar la cuenta de las respuestas correctas
const [incorrectAnswers, setIncorrectAnswers] = useState(0); // Nuevo estado para llevar la cuenta de las respuestas incorrectas
const [totalTime, setTotalTime] = useState(0);


const[isGameEnded, setIsGameEnded] = useState(false); // Nuevo estado para controlar si el juego ha terminado o no
const resetTimer = useRef(null); // Ref para almacenar la función resetTimer
Expand Down Expand Up @@ -112,14 +115,11 @@ export function QuestionArea({questions,setTotalCorrectAnswers, setTotalIncorrec
maxH="80vh" maxW="70vW" minH="70vh" minW="60vW">

<Box display="flex" borderBottom="0.1em solid #000">
<Timer onTimeout={handleTimeout} resetTimer={resetTimer} timeout={30000} />
<EnunciadoBlock pregunta={questionData?.pregunta} />
<Timer darkMode={darkMode} onTimeout={handleTimeout} resetTimer={resetTimer} timeout={timeToAnswer} />
<EnunciadoBlock darkMode={darkMode} pregunta={questionData?.pregunta} />
<GameTimer darkMode={darkMode} isGameEnded={isGameEnded} setTotalTime={setTotalTime}/>
</Box>
<AnswersBlock respuestas={respuestas} correcta={correcta} onAnswerSelect={handleAnswerSelect} isGameEnded={isGameEnded} />




<AnswersBlock darkMode={darkMode} respuestas={respuestas} correcta={correcta} onAnswerSelect={handleAnswerSelect} isGameEnded={isGameEnded}/>
</Box>
)
}
33 changes: 33 additions & 0 deletions webapp/src/components/game/timers/GameTimer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { useEffect, useState } from 'react';
import { Box, Text } from '@chakra-ui/react';
import { FaRegClock } from 'react-icons/fa';



export function GameTimer({darkMode, isGameEnded, setTotalTime }) {
const [seconds, setSeconds] = useState(0);

useEffect(() => {
if (isGameEnded) {
setTotalTime(seconds);
return;
}

const intervalId = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);

return () => clearInterval(intervalId);
}, [isGameEnded, seconds, setTotalTime]);

let textColor = darkMode.darkMode? "#FCFAF0" : "#08313A";

return (
<Box display="flex" alignItems="baseline">
<Box display="flex" alignItems="center" margin="1em">
<FaRegClock style={{ marginRight: "1em", color:{textColor}}} size="2em" />
<Text color={textColor} fontSize="1.3em"> {seconds} </Text>
</Box>
</Box>
);
}
59 changes: 59 additions & 0 deletions webapp/src/components/game/timers/Timer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useEffect, useState, useRef } from 'react';
import { CircularProgress, CircularProgressLabel, useColorModeValue } from '@chakra-ui/react';
import { Box } from "@chakra-ui/react";

export const Timer = React.memo(({darkMode, onTimeout, timeout = 30000, resetTimer }) => {
const [progress, setProgress] = useState(100); // Inicia el progreso en 100%
const intervalRef = useRef(null);

useEffect(() => {
resetTimer.current = resetTimerFunction;
}, []);

useEffect(() => {
const startTime = Date.now();
intervalRef.current = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const remainingTime = timeout - elapsedTime;
const remainingProgress = (remainingTime / timeout) * 100;
setProgress(remainingProgress > 0 ? remainingProgress : 0);
if (remainingProgress <= 0) {
clearInterval(intervalRef.current);
onTimeout();
}
}, 1000); // Actualiza el progreso cada segundo

return () => clearInterval(intervalRef.current);
}, [timeout, onTimeout]);

const resetTimerFunction = () => {
clearInterval(intervalRef.current);
setProgress(100);
const startTime = Date.now();
intervalRef.current = setInterval(() => {
const elapsedTime = Date.now() - startTime;
const remainingTime = timeout - elapsedTime;
const remainingProgress = (remainingTime / timeout) * 100;
setProgress(remainingProgress > 0 ? remainingProgress : 0);
if (remainingProgress <= 0) {
clearInterval(intervalRef.current);
onTimeout();
}
}, 1000); // Actualiza el progreso cada segundo
};

const color = useColorModeValue(
progress > 66.6 ? "green.400" : progress > 33.3 ? "orange.400" : "red.400",
progress > 66.6 ? "green.500" : progress > 33.3 ? "orange.500" : "red.500"
);

let textColor = darkMode.darkMode? "#FCFAF0" : "#08313A";

return (
<Box marginLeft="2em" marginTop="1em" marginBottom="1em">
<CircularProgress value={progress} size="120px" color={color}>
<CircularProgressLabel color={textColor}>{Math.ceil(progress / 100 * timeout / 1000)}s</CircularProgressLabel>
</CircularProgress>
</Box>
);
});
Loading
Loading