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

Merging webapp service into develop branch #101

Merged
merged 23 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d2297a9
What is the f thing with making up the app name?
PabloGOP Mar 1, 2024
ed4f186
Corrected Home Page regarding responsiveness. Pending to check backgr…
PabloGOP Mar 1, 2024
e5aea9f
Merge branch 'develop-users' into develop-webapp
uo288347 Mar 2, 2024
a47e8d1
Mark correct answer when user fails question
uo288347 Mar 2, 2024
5e22d6b
Added icons of correct and incorrect answer
uo288347 Mar 2, 2024
d731c25
Added simple timer
uo288347 Mar 2, 2024
d1b262f
Added time to userdata
uo288347 Mar 2, 2024
2eb9f9f
Disable buttons after answering question
uo288347 Mar 2, 2024
505741c
Display of how many rounds have been answered and how many are left
uo288347 Mar 2, 2024
ea6d322
Changed color of non selected answers
uo288347 Mar 2, 2024
dc40b60
change app document title
uo288347 Mar 2, 2024
669d241
Added background gradient proposal and corrected Instructions styles
PabloGOP Mar 2, 2024
d544e94
Instructions being responsive and show/hide games. Solved footer prob…
PabloGOP Mar 2, 2024
8598eaa
Added pages for the groups addition and list
alagoconde Mar 2, 2024
46319db
Simplified the photos' usage in the instructions and changed the back…
alagoconde Mar 2, 2024
7daf3d7
Merge remote-tracking branch 'origin/develop-users' into develop-webapp
alagoconde Mar 2, 2024
96b0a09
Fix post to /users/edit
uo289689 Mar 3, 2024
85c1698
Add post to /user/edit on the gateway service
uo289689 Mar 3, 2024
b41cfad
Integrated front and back of groups list and addition. It has the bas…
alagoconde Mar 3, 2024
87b0e49
Add possible new favicon
uo288543 Mar 3, 2024
9d1c1b6
Add sound to answers
uo288543 Mar 3, 2024
18f53f4
Add favicon and hover for links in nav
uo288543 Mar 3, 2024
4ac5c1d
Adjusted volume of sounds
uo288543 Mar 3, 2024
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
16 changes: 16 additions & 0 deletions gatewayservice/gateway-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ app.post('/user/add', async (req, res) => {
}
});

app.post('/user/edit', async (req, res) => {
try {
// Forward the add user request to the user service
const userResponse = await axios.post(userServiceUrl + '/user/edit', req.body);
res.json(userResponse.data);
} catch (error) {
if (error.response && error.response.status) {
res.status(error.response.status).json({ error: error.response.data.error });
} else if (error.message) {
res.status(500).json({ error: error.message });
} else {
res.status(500).json({ error: 'Internal Server Error' });
}
}
});

app.get('/group/list', async (req, res) => {
try {
const userResponse = await axios.get(userServiceUrl + '/group/list');
Expand Down
Binary file added webapp/public/defaultFavicon.ico
Binary file not shown.
Binary file modified webapp/public/favicon.ico
Binary file not shown.
Binary file removed webapp/public/gameImg/foto0.jpg
Binary file not shown.
Binary file modified webapp/public/gameImg/foto3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified webapp/public/gameImg/foto4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed webapp/public/gameImg/foto5.jpg
Binary file not shown.
Binary file removed webapp/public/gameImg/foto6.jpg
Binary file not shown.
Binary file removed webapp/public/gameImg/foto7.jpg
Binary file not shown.
Binary file added webapp/public/grey_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/public/hurta2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/public/hurtado.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/public/possibleFavicon.ico
Binary file not shown.
Binary file added webapp/public/possibleIcon.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webapp/public/sounds/success_sound.mp3
Binary file not shown.
Binary file added webapp/public/sounds/wrong_sound.mp3
Binary file not shown.
8 changes: 7 additions & 1 deletion webapp/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Footer from './components/Footer';
import Home from './pages/Home';
import Homepage from './pages/Homepage';
import Game from './pages/Game';
import GroupList from './pages/GroupList';
import GroupCreate from './pages/GroupCreate';
import {Route, Routes} from 'react-router-dom';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import { Box } from '@mui/material';
Expand All @@ -27,6 +29,9 @@ const theme = createTheme({
});

function App() {
React.useEffect(() => {
document.title = "WIQ - Wikidata Infinite Quest";
}, []);
return (
<Box sx={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
<ThemeProvider theme={theme}>
Expand All @@ -38,7 +43,8 @@ function App() {
<Route path="/instructions" element={<Instructions />}/>
<Route path="/homepage" element={<Homepage />}/>
<Route path="/game" element={<Game />}/>

<Route path="/group/list" element={<GroupList />}/>
<Route path="/group/create" element={<GroupCreate />}/>
</Routes>
<Footer/>
</ThemeProvider>
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/components/Footer.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { AppBar, Toolbar, Typography } from '@mui/material';

const Footer = () => {
return (
<AppBar position="static" sx={{ backgroundColor: '#006699', bottom: 0, left: 0, width: '100%', zIndex: 1000 }}>
<AppBar component="footer" position="static" sx={{ backgroundColor: "primary", color: "white", bottom: 0, left: 0, width: '100%', zIndex: 1000 }}>
<Toolbar>
<Typography sx={{ margin: 'auto' }}>
© WIQ_ES04A
Expand Down
5 changes: 2 additions & 3 deletions webapp/src/components/ImageSlider.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useState } from "react";
const slideStyles = {
width: "100%",
height: "100%",
borderRadius: "10px",
backgroundSize: "cover",
backgroundPosition: "center",
};
Expand Down Expand Up @@ -32,8 +31,8 @@ const leftArrowStyles = {

const sliderStyles = {
position: "relative",
width: "35vw",
height: "20vw",
width: "40vw",
height: "30vh",
};

const dotsContainerStyles = {
Expand Down
10 changes: 6 additions & 4 deletions webapp/src/components/NavBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const pages = [
{ path: '/homepage', text: 'Play' },
{ path: '/statistics', text: 'Statistics' },
{ path: '/instructions', text: 'Instructions' },
{ path: '/group/list', text: 'List Groups' },
{ path: '/group/create', text: 'Create Group' },
// Add an object for each new page
];

Expand All @@ -27,7 +29,7 @@ function NavBar() {

return (
// position="static" => Barra se desplaza con scroll down
<AppBar position="static" sx={{ backgroundColor: '#006699' }}>
<AppBar position="static" >
{/* The Container component is used to limit the maximum width of the content inside the AppBar. It ensures that the content doesn't extend too far horizontally. */}
{/* <Container maxWidth="xl"> */}
{/* disableGutters -> Remove toolbar's padding */}
Expand Down Expand Up @@ -71,20 +73,20 @@ function NavBar() {
))}
</Menu>
</Box>
<Button component={Link} to="/" >
<Button component={Link} to="/" sx={{'&:hover': { backgroundColor: '#5f7e94'},}}>
<img src="/white_logo.png" alt="Logo" style={{ height: 40 }} />
</Button>
{/* Pages list in NavBar, only displayed when menu button is not, i.e., in larger devices */}
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
{pages.map((page) => (
<Button component={Link} to={page.path} key={page.path} sx={{ color: 'white', display: 'block' }}>
<Button component={Link} to={page.path} key={page.path} sx={{ color: 'white', display: 'block','&:hover': { backgroundColor: '#5f7e94', },}}>
{page.text}
</Button>
))}
</Box>
{/* Pending: auth depending: if not auth: log in else: menu */}

<Button component={Link} to={'/login'} sx={{ p: 0, display: 'flex', alignItems: 'center', flexGrow: 0 }} >
<Button component={Link} to={'/login'} sx={{ p: 0, display: 'flex', alignItems: 'center', flexGrow: 0, '&:hover': { backgroundColor: '#5f7e94', }}} >
<Typography variant="body2" sx={{ color: 'white', textDecoration: 'none' }}>
Log In
</Typography>
Expand Down
12 changes: 6 additions & 6 deletions webapp/src/data/gameInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"nombre": "WISE MEN STACK",
"descripcion":
"The player chooses a topic from five available options and must answer a battery of questions related to it within 60 seconds. For each question, the host provides two options. If the contestant guesses correctly, they win €20; otherwise, they move on to the next question (as the correct answer would be the other option). If the time runs out before the question is fully asked and both possible answers are provided, the contestant may still answer it; however, if the statement hasn't been completed (or the options weren't provided), they cannot answer.",
"foto": "../gameImg/foto0.jpg"
"foto": "../gameImg/foto1.jpg"
},
{
"nombre": "DISCARDING",
Expand All @@ -18,26 +18,26 @@
{
"nombre": "DISCOVERING CITIES",
"descripcion": "Through photographs and clues provided by Pilar Vázquez, the contestants must guess the city proposed by the program that day. The contestant who goes first after the 'Warm question' will have the right to answer first and for 300 points. If they guess it correctly, they earn the points; if not, there will be a rebound, an additional clue, and a reduction of 100 points for each contestant.",
"foto": "../gameImg/foto3.jpg"
"foto": "../gameImg/foto1.jpg"
},
{
"nombre": "LAST CALL",
"descripcion": "The three contestants are positioned according to the scores obtained so far in the program. Six questions related to a specific topic are asked. Before starting, the answers to the six questions are provided. If the contestant's answer is incorrect, €100 is deducted from their score, and it contributes to a jackpot (which starts at zero). If the contestant answers correctly, they add the amount accumulated in the jackpot to their score, and the jackpot resets to zero (if there is nothing accumulated, the score remains the same). The first question is directed to the participant with the highest score, the next to the second highest, and the third to the lowest scorer. For the next three questions, this order is repeated.",
"foto": "../gameImg/foto4.jpg"
"foto": "../gameImg/foto1.jpg"
},
{
"nombre": "THE HUMAN CALCULATOR",
"descripcion": "Introduced in 2003, this challenge is the most feared after the main game. In it, the contestant who placed second in Last Call (previously, the loser in The Duel) faces the resolution of 7 arithmetic operations in 30 seconds. In 2021, it was slightly modified to include operations with indirect numbers (e.g., years in a lustrum, dozens, elements that make up something...). If they solve them, they keep the points earned for the day, but if not, they lose all accumulated points for the day (since the introduction of the mixed term, they lose 50% of the day's accumulation).",
"foto": "../gameImg/foto5.jpg"
"foto": "../gameImg/foto3.jpg"
},
{
"nombre": "THE PART FOR THE WHOLE",
"descripcion": "This challenge allows one of the contestants to win an extra sum of money, directly added to their total accumulated. The contestants must discover a 'whole', related to each of the 'parts' provided by Elisenda as clues. For their answer to be considered valid, besides discovering the whole, they must describe correctly how each of the parts relates to that whole. All contestants can participate, but in a specific order depending on the score obtained during the program. The game can last for several days until a contestant has the correct and complete answer. First day, the prize starts at 1000 euros, and decreases by 100 euros for each day that passes.",
"foto": "../gameImg/foto6.jpg"
"foto": "../gameImg/foto1.jpg"
},
{
"nombre": "THE CHALLENGE",
"descripcion": "In the early days of the program, there was no challenge, so after the hot question, there was always an elimination. This challenge was incorporated in 1998. It is one of the audience's favorite challenges, despite being an elimination round. The contestant with the lowest score after the last call (previously, the contestant with the lowest score after the hot question) gains access to this challenge. The contestant is presented with seven words, of which they only know the first three letters, and with Elisenda Roca's definitions, they must guess all the words in less than 50 seconds. If they guess them correctly, they will participate in the next program; otherwise, their participation ends. In both cases, the points earned that day are added to the total.",
"foto": "../gameImg/foto7.jpg"
"foto": "../gameImg/foto4.jpg"
}
]
8 changes: 3 additions & 5 deletions webapp/src/index.css
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
body {
background-image: url('../public/hurta2.jpg');
background-size: cover;
background-repeat: no-repeat;
margin: 0;
font-family: -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;
}

code {
Expand Down
1 change: 0 additions & 1 deletion webapp/src/pages/AddUser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// src/components/AddUser.js
import React, { useState } from 'react';
import axios from 'axios';
import { Container, Typography, TextField, Button, Snackbar, Box, Divider } from '@mui/material';
Expand Down
107 changes: 74 additions & 33 deletions webapp/src/pages/Game.js
Original file line number Diff line number Diff line change
@@ -1,67 +1,96 @@
import * as React from 'react';
import { Container, Button, CssBaseline, Grid, Typography, CircularProgress } from '@mui/material';
import CheckIcon from '@mui/icons-material/Check';
import ClearIcon from '@mui/icons-material/Clear';
import questions from "../data/__questions.json"; //static questions battery, we have to change it
import { useNavigate } from 'react-router-dom';
import axios from 'axios';

const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000';

const Game = () => {
const navigate = useNavigate();
const MAX_ROUNDS = 3;
const SUCCESS_SOUND_ROUTE = "/sounds/success_sound.mp3";
const FAILURE_SOUND_ROUTE = "/sounds/wrong_sound.mp3";

// state initialization
const [round, setRound] = React.useState(1);
const [questionData, setQuestionData] = React.useState(null);
const [buttonStates, setButtonStates] = React.useState([]);
const [answered, setAnswered] = React.useState(false);
const [shouldRedirect, setShouldRedirect] = React.useState(false);
const [userData, setUserData] = React.useState({
username: "Samu11", //change it
total_score: 0,
correctly_answered_questions: 0,
incorrectly_answered_questions: 0,
total_time_played: 3600, //change it
total_time_played: 0,
games_played: 1,
});
});
const [timerRunning, setTimerRunning] = React.useState(true); // indicate if the timer is working

// hook to iniciate timer
React.useEffect(() => {
let timer;
if (timerRunning) {
timer = setInterval(() => {
setUserData((prevUserData) => ({
...prevUserData,
total_time_played: prevUserData.total_time_played + 1
}));
}, 1000);
}
return () => clearInterval(timer);
}, [timerRunning]);

// hook to initiating new rounds if the current number of rounds is less than or equal to 3
React.useEffect(() => {
if (round <= 3) {
if (round <= MAX_ROUNDS) {
startNewRound();
} else {
setTimerRunning(false);
setShouldRedirect(true);
}
}, [round]);

// selects a random question from the data and initializes button states for the selected question
const startNewRound = () => {
setAnswered(false);
const randomIndex = Math.floor(Math.random() * questions.length);
setQuestionData(questions[randomIndex]);
setButtonStates(new Array(questions[randomIndex].options.length).fill(null));
};

// this function is called when a user selects a response.
const selectResponse = async (index, response) => {
setAnswered(true);
const newButtonStates = [...buttonStates];

//check answer
if (response === questionData.correctAnswer) {
for (let i = 0; i < questionData.options.length; i++) {
if (i === index) {
newButtonStates[i] = "success";
}
}
newButtonStates[index] = "success"
const sucessSound = new Audio(SUCCESS_SOUND_ROUTE);
sucessSound.volume = 0.40;
sucessSound.play();
setUserData((prevUserData) => ({
...prevUserData,
correctly_answered_questions: prevUserData.correctly_answered_questions + 1,
incorrectly_answered_questions: prevUserData.incorrectly_answered_questions,
total_score: prevUserData.total_score + 20,
games_played: prevUserData.games_played,
}));
} else {
newButtonStates[index] = "failure";
const failureSound = new Audio(FAILURE_SOUND_ROUTE);
failureSound.volume = 0.40;
failureSound.play();
for (let i = 0; i < questionData.options.length; i++) {
if (questionData.options[i] === questionData.correctAnswer) {
newButtonStates[i] = "success";
}
}
setUserData(prevUserData => ({
...prevUserData,
correctly_answered_questions: prevUserData.correctly_answered_questions,
incorrectly_answered_questions: prevUserData.incorrectly_answered_questions + 1,
total_score: prevUserData.total_score,
games_played: prevUserData.games_played,
}));
}

Expand All @@ -70,22 +99,10 @@ const Game = () => {
if (round >= 3) {
// Update user data before redirecting
try {
const response = await fetch('/user/edit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});

if (response.ok) {
console.log('User data updated successfully');
} else {
console.error('Failed to update user data:', response.statusText);
}
} catch (error) {
console.error('Error updating user data:', error);
}
await axios.post(`${apiEndpoint}/user/edit`, userData);
} catch (error) {
console.error("Error:", error);
}
}

setTimeout(() => {
Expand Down Expand Up @@ -118,7 +135,7 @@ const Game = () => {
if (shouldRedirect) {
// Redirect after 3 seconds
setTimeout(() => {
navigate('/');
navigate('/homepage');
}, 4000);

return (
Expand All @@ -133,7 +150,17 @@ if (shouldRedirect) {
}}
>
<CssBaseline />
<p>Game Over</p>
<Typography
variant="h4"
sx={{
color: userData.correctly_answered_questions > userData.incorrectly_answered_questions ? 'green' : 'red',
fontSize: '4rem', // Tamaño de fuente
marginTop: '20px', // Espaciado superior
marginBottom: '50px', // Espaciado inferior
}}
>
{userData.correctly_answered_questions > userData.incorrectly_answered_questions ? "Great Job!" : "Game Over"}
</Typography>
<div>
<Typography variant="h6">Correct Answers: {userData.correctly_answered_questions}</Typography>
<Typography variant="h6">Incorrect Answers: {userData.incorrectly_answered_questions}</Typography>
Expand All @@ -155,6 +182,19 @@ if (shouldRedirect) {
}}
>
<CssBaseline />
<Typography
variant="h6"
sx={{
position: 'absolute',
top: '10%',
right: '5%',
}}
>
Time: {userData.total_time_played} s
</Typography>
<Typography variant='h6' >
{round} / {MAX_ROUNDS}
</Typography>
<Typography variant="h5" mb={2}>
{questionData.question}
</Typography>
Expand All @@ -164,19 +204,20 @@ if (shouldRedirect) {
<Button
variant="contained"
onClick={() => selectResponse(index, option)}
disabled={buttonStates[index] !== null}
disabled={buttonStates[index] !== null || answered} // before, you could still press more than one button
sx={{
height: "50px", // Ajusta el tamaño según sea necesario
width: "50%", // Ajusta el ancho según sea necesario
borderRadius: "20px", // Ajusta el radio según sea necesario
margin: "5px",
backgroundColor: buttonStates[index] === "success" ? "green" : buttonStates[index] === "failure" ? "red" : null,
"&:disabled": {
backgroundColor: buttonStates[index] === "success" ? "green" : buttonStates[index] === "failure" ? "red" : null,
backgroundColor: buttonStates[index] === "success" ? "green" : buttonStates[index] === "failure" ? "red" : "gray",
color: "white",
},
}}
>
{buttonStates[index] === "success" ? <CheckIcon /> : buttonStates[index] === "failure" ? <ClearIcon /> : null}
{option}
</Button>
</Grid>
Expand Down
Loading
Loading