diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 41540bf..17d7417 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -110,10 +110,10 @@ app.post('/imgs/answer', async (req, res) => { }); -app.get('/rankings', async (req, res) => { +app.get('/rankings/:filter', async (req, res) => { try { // Forward the request to the user service - const userResponse = await axios.get(userServiceUrl+'/rankings', req.body); + const userResponse = await axios.get(userServiceUrl+'/rankings/' + req.params.filter, req.body); res.json(userResponse.data); } catch (error) { res.status(error.response.status).json({ error: error.response.data.error }); diff --git a/users/userservice/package-lock.json b/users/userservice/package-lock.json index f21b26c..56afbed 100644 --- a/users/userservice/package-lock.json +++ b/users/userservice/package-lock.json @@ -12,7 +12,8 @@ "bcrypt": "^5.1.1", "body-parser": "^1.20.2", "express": "^4.18.2", - "mongoose": "^8.0.4" + "mongoose": "^8.0.4", + "ramda": "^0.29.1" }, "devDependencies": { "jest": "^29.7.0", @@ -4524,6 +4525,15 @@ "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", "dev": true }, + "node_modules/ramda": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", + "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/users/userservice/package.json b/users/userservice/package.json index 3da52b0..2113d2f 100644 --- a/users/userservice/package.json +++ b/users/userservice/package.json @@ -21,7 +21,8 @@ "bcrypt": "^5.1.1", "body-parser": "^1.20.2", "express": "^4.18.2", - "mongoose": "^8.0.4" + "mongoose": "^8.0.4", + "ramda": "^0.29.1" }, "devDependencies": { "jest": "^29.7.0", diff --git a/users/userservice/user-model.js b/users/userservice/user-model.js index 5654d84..f7334d6 100644 --- a/users/userservice/user-model.js +++ b/users/userservice/user-model.js @@ -18,12 +18,102 @@ const userSchema = new mongoose.Schema({ type: Date, default: Date.now, }, - points: { - type: Number, - default: function() { - // Generate a random integer between 0 and 100 - return Math.floor(Math.random() * 101); - } + + ranking: { + global: { + points: { + type: Number, + default: 0, + }, + questions: { + type: Number, + default: 0, + }, + correct: { + type: Number, + default: 0, + }, + wrong: { + type: Number, + default: 0, + }, + }, + + flags: { + points: { + type: Number, + default: 0, + }, + questions: { + type: Number, + default: 0, + }, + correct: { + type: Number, + default: 0, + }, + wrong: { + type: Number, + default: 0, + }, + }, + + cities: { + points: { + type: Number, + default: 0, + }, + questions: { + type: Number, + default: 0, + }, + correct: { + type: Number, + default: 0, + }, + wrong: { + type: Number, + default: 0, + }, + }, + + monuments: { + points: { + type: Number, + default: 0, + }, + questions: { + type: Number, + default: 0, + }, + correct: { + type: Number, + default: 0, + }, + wrong: { + type: Number, + default: 0, + }, + }, + + food: { + points: { + type: Number, + default: 0, + }, + questions: { + type: Number, + default: 0, + }, + correct: { + type: Number, + default: 0, + }, + wrong: { + type: Number, + default: 0, + }, + }, } }); diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index c85da76..305cbbe 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -3,6 +3,7 @@ const express = require('express'); const mongoose = require('mongoose'); const bcrypt = require('bcrypt'); const bodyParser = require('body-parser'); +const R = require('ramda'); const User = require('./user-model') @@ -35,22 +36,32 @@ async function getRankingFor(loggedUser) { return { ranking: ranking, points: loggedUser.points, user: loggedUser.username } } -app.get('/rankings', async (req, res) => { +app.get('/rankings/:filter', async (req, res) => { try { /* const { token } = req.cookies const decoded = jwt.verify(token, 'your-secret-key') const userId = decoded.userId const loggedUser = await User.findById(userId) const userRanking = getRankingFor(loggedUser) */ - const usersRanking = (await User.find().sort({points: -1})).map( (user, index) => { - return { - ranking: index+1, - points: user.points, - user: user.username } + + const category = req.params.filter; + const usersRanking = (await User.find()); + const ascendingUsers = R.sortBy(R.prop("ranking." + category + ".points"), usersRanking); + const sortedUsers = R.reverse(ascendingUsers); + const sortedRanking = sortedUsers.map( (user, index) => { + return { + // User global data + name: user.username, + position: index+1, + points: user.ranking[category].points, + questions: user.ranking[category].questions, + correct: user.ranking[category].correct, + wrong: user.ranking[category].wrong + } }) //res.json(userRanking, usersRanking) - res.json(usersRanking) + res.json(sortedRanking) } catch (error) { res.status(400).json({ error: error.message }); } diff --git a/webapp/src/App.jsx b/webapp/src/App.jsx index d967959..80e81ba 100644 --- a/webapp/src/App.jsx +++ b/webapp/src/App.jsx @@ -3,7 +3,7 @@ import {BrowserRouter, Routes, Route} from 'react-router-dom' import AddUser from './components/AddUser'; import Navbar from './components/Navbar'; import Login from './components/Login'; -import Rankings from './components/Rankings'; +import RankingsLayout from './components/ranking/RankingLayout'; import Game from './components/Game'; import MainPage from './components/MainPage'; @@ -21,7 +21,7 @@ function App() { }/> } /> } /> - } /> + } /> } /> diff --git a/webapp/src/components/Rankings.jsx b/webapp/src/components/Rankings.jsx deleted file mode 100644 index 9cdc79d..0000000 --- a/webapp/src/components/Rankings.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import axios from 'axios'; - -const apiEndpoint = process.env.REACT_APP_API_ENDPOINT ||'http://localhost:8000'; - -const Rankings = () => { - const [users, setUsers] = useState([]); - - useEffect(() => { - const fetchData = async () => { - try { - const response = await axios.get(`${apiEndpoint}/rankings`); - setUsers(response.data); - } catch (error) { - console.error('Error fetching data:', error); - } - }; - - fetchData(); - }, []); - - return ( -
-
-

Rankings

-
    - {users.map(user => ( -
  • -
    - {user.ranking} - {user.user} -
    - {user.points} points -
  • - ))} -
-
-
- ) -} - -export default Rankings \ No newline at end of file diff --git a/webapp/src/components/ranking/RankingLayout.jsx b/webapp/src/components/ranking/RankingLayout.jsx new file mode 100644 index 0000000..63377d3 --- /dev/null +++ b/webapp/src/components/ranking/RankingLayout.jsx @@ -0,0 +1,56 @@ +import React, { useState, useEffect } from "react"; +import RankingsTable from "./RankingTable"; + +const RankingsLayout = () => { + const [filter, setFilter] = useState('global'); // default ranking = global + + const handleFilterClick = (filter) => { + setFilter(filter); + } + + return ( +
+ + +
+
+

{filter}

+
+
+ +
+
+
+ ) +} + +export default RankingsLayout; \ No newline at end of file diff --git a/webapp/src/components/ranking/RankingTable.jsx b/webapp/src/components/ranking/RankingTable.jsx new file mode 100644 index 0000000..e5a2fb0 --- /dev/null +++ b/webapp/src/components/ranking/RankingTable.jsx @@ -0,0 +1,60 @@ +import React, { useState, useEffect } from "react"; +import axios from "axios"; + +const apiEndpoint = process.env.REACT_APP_API_ENDPOINT ||'http://localhost:8000'; + +const RankingsTable = ({ filter }) => { + const [users, setUsers] = useState([]); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await axios.get(`${apiEndpoint}/rankings/${filter}`); + setUsers(response.data); + } catch (error) { + console.error('Error fetching data:', error); + } + }; + + fetchData(); + }, [filter]); + + return ( +
+
+
+
+ + + + + + + + + + + + + { + users.map(user => ( + + + + + + + + + )) + } + +
UserPointsQuestionsCorrectWrong
{user.position}{user.name}{user.points}{user.questions}{user.correct}{user.wrong}
+
+
+
+
+ ) +} + +export default RankingsTable; \ No newline at end of file