diff --git a/docker-compose.yml b/docker-compose.yml index a968fee3..3da76ec3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,14 +28,12 @@ services: build: ./statsservice depends_on: - mongodb - - userservice ports: - "8004:8004" networks: - mynetwork environment: - MONGODB_URI: mongodb://mongodb:27017/userdb - USER_SERVICE_URL: http://userservice:8001 + MONGODB_URI: mongodb://mongodb:27017/statsdb authservice: container_name: authservice-${teamname:-defaultASW} diff --git a/gatewayservice/gateway-service.js b/gatewayservice/gateway-service.js index 0fcc3d1c..427bd2f1 100644 --- a/gatewayservice/gateway-service.js +++ b/gatewayservice/gateway-service.js @@ -10,7 +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.QUESTION_SERVICE_URL || "http://localhost:8003"; -const statsServiceUrl = process.env.AUTH_SERVICE_URL || "http://localhost:8004"; +const statsServiceUrl = process.env.STATS_SERVICE_URL || "http://localhost:8004"; app.use(cors()); app.use(express.json()); @@ -112,10 +112,9 @@ app.post("/saveGame", async (req, res) => { } }); -app.get("/getstats", async (req, res) => { +app.get("/ranking", async (req, res) => { try { - // Forward the stats request to the stats service - const statsResponse = await axios.get(userServiceUrl + "/getstats", { + const statsResponse = await axios.get(statsServiceUrl + "/ranking", { params: req.query, }); res.json(statsResponse.data); @@ -126,21 +125,6 @@ app.get("/getstats", async (req, res) => { } }); -app.post("/userSaveGame", async (req, res) => { - try { - // Forward the save game request to the stats service - const gameResponse = await axios.post( - userServiceUrl + "/userSaveGame", - req.body - ); - res.json(gameResponse.data); - } catch (error) { - res - .status(error.response.status) - .json({ error: error.response.data.error }); - } -}); - // Start the gateway service const server = app.listen(port, () => { console.log(`Gateway Service listening at http://localhost:${port}`); diff --git a/package-lock.json b/package-lock.json index 477baab9..fbee59f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "wiq_es1a", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { @@ -18078,5 +18078,389 @@ "url": "https://github.com/sponsors/sindresorhus" } } + }, + "dependencies": { + "@next/env": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz", + "integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==", + "peer": true + }, + "@next/swc-darwin-arm64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz", + "integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==", + "optional": true, + "peer": true + }, + "@next/swc-darwin-x64": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz", + "integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==", + "optional": true, + "peer": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz", + "integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==", + "optional": true, + "peer": true + }, + "@next/swc-linux-arm64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz", + "integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==", + "optional": true, + "peer": true + }, + "@next/swc-linux-x64-gnu": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz", + "integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==", + "optional": true, + "peer": true + }, + "@next/swc-linux-x64-musl": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz", + "integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==", + "optional": true, + "peer": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz", + "integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==", + "optional": true, + "peer": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz", + "integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==", + "optional": true, + "peer": true + }, + "@next/swc-win32-x64-msvc": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz", + "integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==", + "optional": true, + "peer": true + }, + "@swc/helpers": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "peer": true, + "requires": { + "tslib": "^2.4.0" + } + }, + "@types/luxon": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.8.tgz", + "integrity": "sha512-jYvz8UMLDgy3a5SkGJne8H7VA7zPV2Lwohjx0V8V31+SqAjNmurWMkk9cQhfvlcnXWudBpK9xPM1n4rljOcHYQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "requires": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "peer": true, + "requires": { + "streamsearch": "^1.1.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001583", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001583.tgz", + "integrity": "sha512-acWTYaha8xfhA/Du/z4sNZjHUWjkiuoAi2LM+T/aL+kemKQgPT1xBb/YKjlQ0Qo8gvbHsGNplrEJ+9G3gL7i4Q==", + "peer": true + }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "peer": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "cron": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/cron/-/cron-3.1.6.tgz", + "integrity": "sha512-cvFiQCeVzsA+QPM6fhjBtlKGij7tLLISnTSvFxVdnFGLdz+ZdXN37kNe0i2gefmdD17XuZA6n2uPVwzl4FxW/w==", + "requires": { + "@types/luxon": "~3.3.0", + "luxon": "~3.4.0" + } + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, + "date-fns": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.3.1.tgz", + "integrity": "sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "peer": true + }, + "js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "luxon": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.4.4.tgz", + "integrity": "sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==" + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "peer": true + }, + "next": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz", + "integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==", + "peer": true, + "requires": { + "@next/env": "14.1.0", + "@next/swc-darwin-arm64": "14.1.0", + "@next/swc-darwin-x64": "14.1.0", + "@next/swc-linux-arm64-gnu": "14.1.0", + "@next/swc-linux-arm64-musl": "14.1.0", + "@next/swc-linux-x64-gnu": "14.1.0", + "@next/swc-linux-x64-musl": "14.1.0", + "@next/swc-win32-arm64-msvc": "14.1.0", + "@next/swc-win32-ia32-msvc": "14.1.0", + "@next/swc-win32-x64-msvc": "14.1.0", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + } + }, + "node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "peer": true + }, + "postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "peer": true, + "requires": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-auth-kit": { + "version": "3.0.2-alpha.19", + "resolved": "https://registry.npmjs.org/react-auth-kit/-/react-auth-kit-3.0.2-alpha.19.tgz", + "integrity": "sha512-xZStv05wItit/AN2qgSErYCtpn9u8rr3cpgMxG8ZOkJMcYa4wvFPkYoAWT5rkOuP85fp9B0AqGb5tUen0B5yeg==", + "requires": { + "js-cookie": "^3.0.1", + "rxjs": "^7.8.1" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "peer": true + }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "peer": true + }, + "styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "peer": true, + "requires": { + "client-only": "0.0.1" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + } } } diff --git a/statsservice/model/partidas.json b/statsservice/model/partidas.json deleted file mode 100644 index 63dc9b4f..00000000 --- a/statsservice/model/partidas.json +++ /dev/null @@ -1,38 +0,0 @@ -[ - { - "username": "vagrant", - "points": 200, - "correctQuestions": 7, - "incorrectQuestions": 3 - }, - { - "username": "vagrant", - "points": 400, - "correctQuestions": 16, - "incorrectQuestions": 4 - }, - { - "username": "jose123", - "points": 200, - "correctQuestions": 8, - "incorrectQuestions": 2 - }, - { - "username": "vagrant", - "points": 1000, - "correctQuestions": 40, - "incorrectQuestions": 10 - }, - { - "username": "rober200", - "points": 100, - "correctQuestions": 3, - "incorrectQuestions": 2 - }, - { - "username": "jose123", - "points": 40, - "correctQuestions": 1, - "incorrectQuestions": 1 - } -] \ No newline at end of file diff --git a/statsservice/model/stats-getter.js b/statsservice/model/stats-getter.js index 86d09305..cf47411d 100644 --- a/statsservice/model/stats-getter.js +++ b/statsservice/model/stats-getter.js @@ -1,50 +1,83 @@ -const User = require('../../users/userservice/user-model.js'); +const Stats = require('./stats-model.js'); class StatsForUser { - async getStatsForUser(username){ - try { - const user = await User.findOne({ username: username }); - - if (!user) { - throw new Error('Usuario no encontrado'); + async getStatsForUser(username,gamemode){ + var statsJSON=null; + if(gamemode=="clasico" || gamemode=="bateria"){ + statsJSON = await this.getStats(username,gamemode); } + return statsJSON; + + } - const partidas = user.games; - - var nGamesPlayed = partidas.length; - var totalPoints = 0; - var totalCorrectQuestions = 0; - var totalIncorrectQuestions = 0; - - for (const partida of partidas){ - totalPoints += partida.points; - totalCorrectQuestions += partida.correctAnswers; - totalIncorrectQuestions += partida.incorrectAnswers; + async getStats(username,gamemode){ + try { + var stats = await Stats.findOne({ username:username,gamemode:gamemode }); + + if (stats) { + return { + username: stats.username, + gamemode: stats.gamemode, + nGamesPlayed: stats.nGamesPlayed, + avgPoints: stats.avgPoints, + totalPoints: stats.totalPoints, + totalCorrectQuestions: stats.totalCorrectQuestions, + totalIncorrectQuestions: stats.totalIncorrectQuestions, + ratioCorrect: stats.ratioCorrect, + avgTime: stats.avgTime + }; + } else { + return null; // Si no se encuentran estadísticas para el usuario } + } catch (error) { + console.error('Error al obtener estadísticas:', error); + throw error; + } + } - const avgPoints = nGamesPlayed > 0 ? - totalPoints / nGamesPlayed : 0; + async calculateStats(username,gamemode,gameData,stats){ - const ratioCorrectToIncorrect = totalIncorrectQuestions !== 0 ? - totalCorrectQuestions / totalIncorrectQuestions : totalCorrectQuestions; + + const totalGamesPlayed = stats.nGamesPlayed + 1; + const newAvgPoints = (stats.avgPoints * stats.nGamesPlayed + gameData.points) / totalGamesPlayed; + const newTotalPoints = stats.totalPoints + gameData.points; + const newTotalCorrectQuestions = stats.totalCorrectQuestions + gameData.correctAnswers; + const newTotalIncorrectQuestions = stats.totalIncorrectQuestions + gameData.incorrectAnswers; + const newRatioCorrect = (newTotalCorrectQuestions / (newTotalIncorrectQuestions+newTotalCorrectQuestions))*100; + const newAvgTime = (stats.avgTime * stats.nGamesPlayed + gameData.avgTime) / totalGamesPlayed; - const statsJSON = { - username: username, - nGamesPlayed: nGamesPlayed, - avgPoints: avgPoints, - totalPoints: totalPoints, - totalCorrectQuestions: totalCorrectQuestions, - totalIncorrectQuestions: totalIncorrectQuestions, - ratioCorrectToIncorrect: ratioCorrectToIncorrect - }; + return { + username:username, + gamemode: gamemode, + nGamesPlayed: totalGamesPlayed, + avgPoints: newAvgPoints, + totalPoints: newTotalPoints, + totalCorrectQuestions: newTotalCorrectQuestions, + totalIncorrectQuestions: newTotalIncorrectQuestions, + ratioCorrect: newRatioCorrect, + avgTime: newAvgTime + }; + } - return statsJSON; + async getRanking(gamemode) { + try { + var stats = await Stats.find({gamemode:gamemode}).sort({ avgPoints: -1 }).limit(10); + + if (stats && stats.length > 0) { + return stats.map(stat => ({ + username: stat.username, + avgPoints: stat.avgPoints + })); + } else { + return null; + } } catch (error) { - console.error('Error al obtener las estadísticas del usuario:', error); + console.error('Error al obtener estadísticas:', error); throw error; } } + } module.exports = StatsForUser; diff --git a/statsservice/model/stats-model.js b/statsservice/model/stats-model.js new file mode 100644 index 00000000..a05a66a9 --- /dev/null +++ b/statsservice/model/stats-model.js @@ -0,0 +1,44 @@ +const mongoose = require('mongoose'); + +const stats = new mongoose.Schema({ + username: { + type: String, + required: true, + }, + gamemode: { + type: String, + required: true, + }, + nGamesPlayed: { + type: Number, + required: true, + }, + avgPoints: { + type: Number, + required: true, + }, + totalPoints: { + type: Number, + required: true, + }, + totalCorrectQuestions: { + type: Number, + required: true, + }, + totalIncorrectQuestions: { + type: Number, + required: true, + }, + ratioCorrect: { + type: Number, + required: true, + }, + avgTime: { + type: Number, + required: true, + }, +}); + +const Stats = mongoose.model('Stats', stats); + +module.exports = Stats diff --git a/statsservice/package-lock.json b/statsservice/package-lock.json index 133a00de..d9bd0982 100644 --- a/statsservice/package-lock.json +++ b/statsservice/package-lock.json @@ -9,10 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "axios": "^1.6.7", "cors": "^2.8.5", - "express": "^4.18.2", "mongoose": "^8.2.0" } @@ -55,7 +53,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -71,7 +68,6 @@ "proxy-from-env": "^1.1.0" } }, - "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -99,7 +95,6 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/bson/-/bson-6.4.0.tgz", "integrity": "sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==", - "engines": { "node": ">=16.20.1" } @@ -710,7 +705,6 @@ "node": ">=0.10.0" } }, - "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", diff --git a/statsservice/stats-service.js b/statsservice/stats-service.js index b6f8fdb0..88ee82be 100644 --- a/statsservice/stats-service.js +++ b/statsservice/stats-service.js @@ -3,52 +3,97 @@ const express = require("express"); const bodyParser = require("body-parser"); const cors = require('cors'); const axios = require('axios'); +const Stats = require("./model/stats-model.js"); +const StatsForUser = require("./model/stats-getter.js"); +const mongoose = require('mongoose'); + +const statsGetter= new StatsForUser(); const app = express(); const port = 8004; -const userServiceUrl = process.env.USER_SERVICE_URL || "http://localhost:8001"; +const URL = process.env.GATEWAY_SERVICE_URL || "http://localhost:8000"; app.use(bodyParser.json()); app.use(cors()); app.set("json spaces", 40); +const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/statsdb'; +mongoose.connect(mongoUri); + app.post("/saveGame", async (req, res) => { try { - const { username, correctAnswers, incorrectAnswers, points, avgTime } = req.body; + const username = req.body.username; + const gamemode = req.body.gameMode; + const gameData = req.body.gameData; - // Hacer una solicitud al servicio user-service para guardar el juego - const response = await axios.post(userServiceUrl + '/userSaveGame', { - username, - correctAnswers, - incorrectAnswers, - points, - avgTime - }); + if (gamemode == "clasico" || gamemode == "bateria") { + // Buscar las estadísticas existentes del usuario y modo de juego + let stats = await Stats.findOne({ username: username, gamemode: gamemode }); - res.json(response.data); + if (!stats) { + // Si no existen estadísticas, crear un nuevo documento + stats = new Stats({ + username: username, + gamemode: gamemode, + nGamesPlayed: 1, + avgPoints: gameData.points, + totalPoints: gameData.points, + totalCorrectQuestions: gameData.correctAnswers, + totalIncorrectQuestions: gameData.incorrectAnswers, + ratioCorrect: (gameData.correctAnswers / (gameData.incorrectAnswers + gameData.correctAnswers)) * 100, + avgTime: gameData.avgTime, + }); - user.games.push(newGame); + await stats.save(); + } else { - await user.save(); + await Stats.updateOne( + { username: username, gamemode: gamemode }, + { + $inc: { + nGamesPlayed: 1, + totalPoints: gameData.points, + totalCorrectQuestions: gameData.correctAnswers, + totalIncorrectQuestions: gameData.incorrectAnswers, + }, + $set: { + avgPoints: (stats.avgPoints * stats.nGamesPlayed + gameData.points) / (stats.nGamesPlayed + 1), + ratioCorrect: ((stats.totalCorrectQuestions + gameData.correctAnswers) / (stats.totalIncorrectQuestions + stats.totalCorrectQuestions + gameData.incorrectAnswers + gameData.correctAnswers)) * 100, + avgTime: (stats.avgTime * stats.nGamesPlayed + gameData.avgTime) / (stats.nGamesPlayed + 1), + } + } + ); + } - res.json({ message: "Juego guardado exitosamente" }); + res.json({ message: "Partida guardada exitosamente" }); + } } catch (error) { - res.status(400).json({ error: error.message }); + res.status(400).json({ error: "Error al guardar juego" + error.message }); } }); -// Ruta para obtener estadísticas + app.get("/stats", async (req, res) => { try { - const username = req.query.user; + var data = await statsGetter.getStatsForUser(req.query.user,req.query.gamemode); - // Hacer una solicitud al servicio user-service para obtener estadísticas - const response = await axios.get(userServiceUrl + '/getstats?user=${username}'); + res.json(data); - res.json(response.data); } catch (error) { - res.status(400).json({ error: `Error al obtener las estadísticas: ${error.message}`}); + + res.status(400).json({ error: "Error al obtener las estadísticas:"+error.message }); + } +}); + +app.get("/ranking", async (req, res) => { + try { + var data = await statsGetter.getRanking(req.query.gamemode); + res.json(data); + + } catch (error) { + + res.status(400).json({ error: "Error al obtener el ranking: "+error.message }); } }); @@ -57,5 +102,10 @@ app.get("/stats", async (req, res) => { const server = app.listen(port, () => { console.log(`Stats Service listening at http://localhost:${port}`); }); + +server.on('close', () => { + // Close the Mongoose connection + mongoose.connection.close(); +}); module.exports = server; \ No newline at end of file diff --git a/statsservice/user-model.js b/statsservice/user-model.js deleted file mode 100644 index 3add662e..00000000 --- a/statsservice/user-model.js +++ /dev/null @@ -1,26 +0,0 @@ -const mongoose = require('mongoose'); - -const userSchema = new mongoose.Schema({ - username: { - type: String, - required: true, - }, - password: { - type: String, - required: true, - }, - createdAt: { - type: Date, - default: Date.now, - }, - games: [{ - correctAnswers: Number, - incorrectAnswers: Number, - points: Number, - avgTime: Number - }], -}); - -const User = mongoose.model('User', userSchema); - -module.exports = User \ No newline at end of file diff --git a/users/userservice/game-model.js b/users/userservice/game-model.js deleted file mode 100644 index 04a2aba2..00000000 --- a/users/userservice/game-model.js +++ /dev/null @@ -1,25 +0,0 @@ -const mongoose = require('mongoose'); - -const gameSchema = new mongoose.Schema({ - // Atributos de la partida - correctAnswers: { - type: Number, - default: 0 - }, - incorrectAnswers: { - type: Number, - default: 0 - }, - points: { - type: Number, - default: 0 - }, - avgTime: { - type: Number, - default: 0 - } -}); - -const Game = mongoose.model('Game', gameSchema); - -module.exports = Game; diff --git a/users/userservice/stats-getter.js b/users/userservice/stats-getter.js deleted file mode 100644 index 1a258504..00000000 --- a/users/userservice/stats-getter.js +++ /dev/null @@ -1,57 +0,0 @@ -const User = require('./user-model.js'); - - -class StatsForUser { - - async getStatsForUser(username){ - try { - const user = await User.findOne({ username: username }); - - if (!user) { - throw new Error('Usuario no encontrado'); - } - - const partidas = user.games; - - var nGamesPlayed = partidas.length; - var totalPoints = 0; - var totalCorrectQuestions = 0; - var totalIncorrectQuestions = 0; - var totalTime=0; - - for (const partida of partidas){ - totalPoints += partida.points; - totalCorrectQuestions += partida.correctAnswers; - totalIncorrectQuestions += partida.incorrectAnswers; - totalTime+=partida.avgTime; - } - - var avgPoints = nGamesPlayed > 0 ? - totalPoints / nGamesPlayed : 0; - - var ratioCorrectToIncorrect = totalIncorrectQuestions !== 0 ? - totalCorrectQuestions / totalIncorrectQuestions : totalCorrectQuestions; - - var avgTime = nGamesPlayed > 0 ? totalTime / nGamesPlayed : 0; - - var statsJSON = { - username: username, - nGamesPlayed: nGamesPlayed, - avgPoints: avgPoints, - totalPoints: totalPoints, - totalCorrectQuestions: totalCorrectQuestions, - totalIncorrectQuestions: totalIncorrectQuestions, - ratioCorrectToIncorrect: ratioCorrectToIncorrect, - avgTime: avgTime - }; - - return statsJSON; - } catch (error) { - console.error('Error al obtener las estadísticas del usuario:', error); - throw error; - } - } -} - -module.exports = StatsForUser; - diff --git a/users/userservice/user-service.js b/users/userservice/user-service.js index f735df91..17ac1134 100644 --- a/users/userservice/user-service.js +++ b/users/userservice/user-service.js @@ -4,9 +4,6 @@ const mongoose = require('mongoose'); const bcrypt = require('bcrypt'); const bodyParser = require('body-parser'); const User = require('./user-model'); -const StatsForUser = require("./stats-getter.js"); - -const statsGetter= new StatsForUser(); const app = express(); const port = 8001; @@ -49,49 +46,6 @@ app.post('/adduser', async (req, res) => { res.status(400).json({ error: error.message }); }}); - app.post("/userSaveGame", async (req, res) => { - try { - const { username, correctAnswers, incorrectAnswers, points, avgTime } = req.body; - - // Encontrar al usuario en la base de datos - const user = await User.findOne({ username }); - - if (!user) { - return res.status(404).json({ error: "Usuario no encontrado" }); - } - - // Crear un nuevo juego - const newGame = { - correctAnswers, - incorrectAnswers, - points, - avgTime - }; - - user.games.push(newGame); - - await user.save(); - - res.json({ message: "Partida guardada exitosamente" }); - } catch (error) { - res.status(400).json({ error: "Error al guardar partida"}); - } - }); - - app.get("/getstats", async (req, res) => { - const user = await User.findOne({ username:req.query.user }); - - if (!user) { - return res.status(404).json({ error: "Usuario no encontrado" }); - } - try { - var data = await statsGetter.getStatsForUser(req.query.user); - res.json(data); - } catch (error) { - res.status(400).json({ error: "Error al obtener las estadísticas" }); - } - }); - const server = app.listen(port, () => { console.log(`User Service listening at http://localhost:${port}`); }); diff --git a/webapp/src/App.css b/webapp/src/App.css index 1b3fda63..328ee684 100644 --- a/webapp/src/App.css +++ b/webapp/src/App.css @@ -1,3 +1,4 @@ + #root { height: 100vh; display: flex; diff --git a/webapp/src/components/Login/Login.js b/webapp/src/components/Login/Login.js index 1f89d808..54b5acec 100644 --- a/webapp/src/components/Login/Login.js +++ b/webapp/src/components/Login/Login.js @@ -1,8 +1,8 @@ // src/components/Login.js -import React, { useState } from "react"; -import axios from "axios"; -import { useNavigate } from "react-router-dom"; -import "./Login.css"; +import React, { useState } from 'react'; +import axios from 'axios'; +import {useNavigate} from "react-router-dom"; +import './Login.css'; const Login = () => { const [username, setUsername] = useState(""); diff --git a/webapp/src/components/Stats.js b/webapp/src/components/Stats.js index 34e2b0fd..1c47483f 100644 --- a/webapp/src/components/Stats.js +++ b/webapp/src/components/Stats.js @@ -3,9 +3,10 @@ const axios = require('axios'); // Definir la función para realizar la solicitud async function fetchStats() { const username = localStorage.getItem('username'); - const url = 'http://localhost:8001/getstats'; + const url = 'http://localhost:8004/stats'; const params = { - user: username + user: username, + gamemode: "clasico" }; try { diff --git a/webapp/src/index.css b/webapp/src/index.css index ec2585e8..bf6df07a 100644 --- a/webapp/src/index.css +++ b/webapp/src/index.css @@ -11,3 +11,11 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + +#root { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; +} + diff --git a/webapp/src/pages/Authenticate/Authenticate.css b/webapp/src/pages/Authenticate/Authenticate.css index e1c7f780..7ebe727b 100644 --- a/webapp/src/pages/Authenticate/Authenticate.css +++ b/webapp/src/pages/Authenticate/Authenticate.css @@ -1,3 +1,16 @@ +.auth-container { + text-align: center; + margin-top: 50px; + } + + .auth-title { + margin-bottom: 20px; + } + + .auth-link { + margin-top: 20px; + } + .gotoregister, .gotologin { box-shadow: none; diff --git a/webapp/src/pages/Authenticate/Authenticate.test.js b/webapp/src/pages/Authenticate/Authenticate.test.js new file mode 100644 index 00000000..4a705cfa --- /dev/null +++ b/webapp/src/pages/Authenticate/Authenticate.test.js @@ -0,0 +1,39 @@ +import React from 'react'; +import { render, fireEvent } from '@testing-library/react'; +import Authenticate from './Authenticate'; + +test('renders welcome message', () => { + const { getByText } = render(); + const welcomeMessage = getByText(/Bienvenido a WIQ/i); + expect(welcomeMessage).toBeInTheDocument(); +}); + +test('renders login form by default', () => { + const { getByLabelText } = render(); + const usernameInput = getByLabelText(/username/i); + const passwordInput = getByLabelText(/password/i); + expect(usernameInput).toBeInTheDocument(); + expect(passwordInput).toBeInTheDocument(); +}); + +test('renders registration form when "Regístrate" link is clicked', () => { + const { getByText, getByLabelText } = render(); + const registrationLink = getByText(/¿No tienes cuenta? Regístrate./i); + fireEvent.click(registrationLink); + const usernameInput = getByLabelText(/username/i); + const emailInput = getByLabelText(/email/i); + const passwordInput = getByLabelText(/password/i); + expect(usernameInput).toBeInTheDocument(); + expect(emailInput).toBeInTheDocument(); + expect(passwordInput).toBeInTheDocument(); +}); + +test('renders login form when "Inicia sesión" link is clicked', () => { + const { getByText, getByLabelText } = render(); + const loginLink = getByText(/Ya tienes cuenta? Inicia sesión./i); + fireEvent.click(loginLink); + const usernameInput = getByLabelText(/username/i); + const passwordInput = getByLabelText(/password/i); + expect(usernameInput).toBeInTheDocument(); + expect(passwordInput).toBeInTheDocument(); +}); diff --git a/webapp/src/pages/Bateria/Bateria.css b/webapp/src/pages/Bateria/Bateria.css index 33990a6c..f32898ad 100644 --- a/webapp/src/pages/Bateria/Bateria.css +++ b/webapp/src/pages/Bateria/Bateria.css @@ -66,7 +66,6 @@ button{ will-change: transform; margin: 0; } - /* HTML:
*/ .loader { width: 50px; diff --git a/webapp/src/pages/Bateria/Bateria.js b/webapp/src/pages/Bateria/Bateria.js index dba44bd2..ff6a7a64 100644 --- a/webapp/src/pages/Bateria/Bateria.js +++ b/webapp/src/pages/Bateria/Bateria.js @@ -3,6 +3,7 @@ import "./Bateria.css"; import Nav from "../../components/Nav/Nav.js"; import Footer from "../../components/Footer/Footer.js"; import { Link, useNavigate } from "react-router-dom"; +import axios from 'axios'; const JuegoPreguntas = () => { const URL = process.env.REACT_APP_API_ENDPOINT || "http://localhost:8000" @@ -18,6 +19,11 @@ const JuegoPreguntas = () => { const [progressPercent, setProgressPercent] = useState(100); const navigate = useNavigate(); + //Used for user stats + const [preguntasCorrectas, setPreguntasCorrectas] = useState(0); + const [preguntasFalladas, setPreguntasFalladas] = useState(0); + const [tiempoMedio, setTiempoMedio] = useState(0); + useEffect(() => { fetch(URL + "/questions", { method: "POST", @@ -48,6 +54,8 @@ const JuegoPreguntas = () => { useEffect(() => { if (tiempoRestante === 0) { setJuegoTerminado(true); + + guardarPartida(); } const timer = setInterval(() => { setTiempoRestante((prevTiempo) => (prevTiempo <= 0 ? 0 : prevTiempo - 1)); @@ -55,6 +63,30 @@ const JuegoPreguntas = () => { return () => clearInterval(timer); }, [tiempoRestante]); + const guardarPartida = async () => { + if(preguntasCorrectas+preguntasFalladas>0){ + setTiempoMedio(180/(preguntasCorrectas+preguntasFalladas)); + } + const username = localStorage.getItem("username"); + const newGame = { + username: username, + gameMode: "bateria", + gameData: { + correctAnswers: preguntasCorrectas, + incorrectAnswers: preguntasFalladas, + points: puntuacion, + avgTime: tiempoMedio, + }, + }; + try { + const response = await axios.post(URL + '/saveGame', newGame); + console.log("Solicitud exitosa:", response.data); + + } catch (error) { + console.error('Error al guardar el juego:', error); + } + } + useEffect(() => { setProgressPercent(tiempoRestante / TIME * 100); @@ -65,9 +97,13 @@ const JuegoPreguntas = () => { return () => clearInterval(timer); }, [tiempoRestante]); - const handleSiguientePregunta = (respuesta) => { + const handleSiguientePregunta = async (respuesta) => { if (respuesta === preguntaActual.correcta) { setPuntuacion(puntuacion + 1); + setPreguntasCorrectas(preguntasCorrectas+1); + } + else{ + setPreguntasFalladas(preguntasFalladas+1); } if (indicePregunta + 1 < preguntas.length) { setIndicePregunta(indicePregunta + 1); diff --git a/webapp/src/pages/Clasico/Clasico.css b/webapp/src/pages/Clasico/Clasico.css index 80023370..492466bb 100644 --- a/webapp/src/pages/Clasico/Clasico.css +++ b/webapp/src/pages/Clasico/Clasico.css @@ -84,4 +84,56 @@ button{ animation: l3 1s infinite linear; } @keyframes l3 {to{transform: rotate(1turn)}} - \ No newline at end of file + +.loader { + border: 8px solid #f3f3f3; /* Light grey */ + border-top: 8px solid #3498db; /* Blue */ + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 2s linear infinite; + margin: 50px auto; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.questionContainer { + text-align: center; + margin: 50px auto; +} + +.questionContainer h2 { + font-size: 2em; +} + +.questionContainer p { + font-size: 1.5em; + margin-bottom: 30px; +} + +.responsesContainer { + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +.responsesContainer button { + margin: 10px; + padding: 10px 20px; + background-color: #007bff; + color: white; + border: none; + cursor: pointer; +} + +.responsesContainer button:hover { + background-color: #0056b3; +} + +.timer, .points { + font-size: 1.5em; + margin-top: 20px; +} diff --git a/webapp/src/pages/Clasico/Clasico.js b/webapp/src/pages/Clasico/Clasico.js index 74578981..a1a83a32 100644 --- a/webapp/src/pages/Clasico/Clasico.js +++ b/webapp/src/pages/Clasico/Clasico.js @@ -3,6 +3,7 @@ import "./Clasico.css"; import Nav from "../../components/Nav/Nav.js"; import { Link, useNavigate } from "react-router-dom"; import Footer from "../../components/Footer/Footer.js"; +import axios from 'axios'; const JuegoPreguntas = () => { const URL = process.env.REACT_APP_API_ENDPOINT || "http://localhost:8000"; @@ -109,16 +110,16 @@ const JuegoPreguntas = () => { return {}; }; - const handleSiguientePregunta = async () => { + const handleSiguientePregunta = () => { if (respuestaSeleccionada === preguntaActual.correcta) { setPuntuacion(puntuacion + 1); setPreguntasCorrectas(preguntasCorrectas + 1); + console.log("bien"); } else { setPreguntasFalladas(preguntasFalladas + 1); + console.log("mal"); } - - setTiempoTotal(tiempoTotal + 10 - tiempoRestante); - + setTiempoTotal(tiempoTotal+tiempoRestante); setRespuestaSeleccionada(null); setTiempoRestante(10); setProgressPercent(100); @@ -127,42 +128,38 @@ const JuegoPreguntas = () => { setIndicePregunta(indicePregunta + 1); setPreguntaActual(preguntas[indicePregunta + 1]); } else { - try { - if (preguntasCorrectas + preguntasFalladas > 0) { - setTiempoMedio( - tiempoTotal / (preguntasCorrectas + preguntasFalladas) - ); - } - - //Now we store the game in the user's DB - const username = localStorage.getItem("username"); - const newGame = { - username: username, - correctAnswers: preguntasCorrectas, - incorrectAnswers: preguntasFalladas, - points: puntuacion, - avgTime: tiempoMedio, - }; - - const response = await fetch(URL + "/userSaveGame", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(newGame), - }); - - if (!response.ok) { - throw new Error("Error al guardar el juego"); - } - } catch (error) { - console.error("Error al guardar el juego:", error); - // Manejar el error, por ejemplo, mostrando un mensaje al usuario - } finally { - setJuegoTerminado(true); + setJuegoTerminado(true); + if (preguntasCorrectas + preguntasFalladas > 0) { + setTiempoMedio(tiempoTotal / (preguntasCorrectas + preguntasFalladas)); } + guardarPartida(); } - }; + }; + + const guardarPartida = async () => { + + + //Now we store the game in the stats DB + const username = localStorage.getItem("username"); + const newGame = { + username: username, + gameMode: "clasico", + gameData: { + correctAnswers: preguntasCorrectas, + incorrectAnswers: preguntasFalladas, + points: puntuacion, + avgTime: tiempoMedio, + }, + }; + + try { + const response = await axios.post(URL + '/saveGame', newGame); + console.log("Solicitud exitosa:", response.data); + + } catch (error) { + console.error('Error al guardar el juego:', error); + } + } const handleRepetirJuego = () => { // Reiniciar el estado para repetir el juego @@ -216,6 +213,16 @@ const JuegoPreguntas = () => { ))} +
+ +
+ +
Tiempo restante: {tiempoRestante}
Tiempo restante: {Math.floor(tiempoRestante)}
diff --git a/webapp/src/pages/Home/Home.css b/webapp/src/pages/Home/Home.css index 68a3a232..f4436382 100644 --- a/webapp/src/pages/Home/Home.css +++ b/webapp/src/pages/Home/Home.css @@ -1,22 +1,35 @@ -.games-container{ - display: grid; - grid-template-columns: 1fr; - justify-content: center; -} - -ul{ - display: flex; - flex-wrap: wrap; - align-items: center; - grid-template-columns: 1fr; - list-style-type: none; +.games-container { text-align: center; - gap: 1rem; - margin: 1rem; + margin: 50px auto; + } + + .games-container h1 { + font-size: 2.5em; + } + + .games-container h2 { + font-size: 1.5em; + margin-bottom: 30px; + } + + .games-container ul { + list-style: none; padding: 0; -} - -ul li{ + } + + .games-container ul li { + margin-bottom: 10px; + } + + .games-container ul li a { + display: inline-block; + padding: 10px 20px; + background-color: #007bff; + color: white; text-decoration: none; - -} \ No newline at end of file + } + + .games-container ul li a:hover { + background-color: #0056b3; + } + \ No newline at end of file diff --git a/webapp/src/pages/Home/home.test.js b/webapp/src/pages/Home/home.test.js new file mode 100644 index 00000000..6128c2cf --- /dev/null +++ b/webapp/src/pages/Home/home.test.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import Home from './Home'; +import { BrowserRouter as Router } from 'react-router-dom'; + +test('renders welcome message', () => { + render( + + + + ); + const welcomeElement = screen.getByText(/Bienvenido/i); + expect(welcomeElement).toBeInTheDocument(); +}); + +test('renders game modes', () => { + render( + + + + ); + const classicLink = screen.getByText(/Clásico/i); + const batteryLink = screen.getByText(/Batería de sabios/i); + const discardingLink = screen.getByText(/Descartando/i); + const hotQuestionLink = screen.getByText(/La pregunta caliente/i); + const discoveringCitiesLink = screen.getByText(/Descubriendo ciudades/i); + + expect(classicLink).toBeInTheDocument(); + expect(batteryLink).toBeInTheDocument(); + expect(discardingLink).toBeInTheDocument(); + expect(hotQuestionLink).toBeInTheDocument(); + expect(discoveringCitiesLink).toBeInTheDocument(); +}); diff --git a/webapp/src/pages/Stats/Stats.js b/webapp/src/pages/Stats/Stats.js index cafb9745..c5560ce6 100644 --- a/webapp/src/pages/Stats/Stats.js +++ b/webapp/src/pages/Stats/Stats.js @@ -8,31 +8,69 @@ const Stats = () => { const [username, setUsername] = useState(localStorage.username); const [stats, setStats] = useState(null); + const [ranking, setRanking] = useState(null); + const [gamemode, setGamemode] = useState("clasico"); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); + const fetchStats = () => { + setIsLoading(true); + fetch(gatewayUrl+`/stats?user=${username}&gamemode=${gamemode}`) + .then((response) => response.json()) + .then((data) => { + setStats(data); + setIsLoading(false); + }) + .catch((error) => { + console.error('Error al obtener las estadísticas:', error); + setError(error.message || 'Ha ocurrido un error al obtener las estadísticas'); + setIsLoading(false); + }); + }; + + const fetchRanking = () => { + setIsLoading(true); + fetch(gatewayUrl+`/ranking?gamemode=${gamemode}`) + .then((response) => response.json()) + .then((data) => { + setRanking(data); + setIsLoading(false); + }) + .catch((error) => { + console.error('Error al obtener el ranking:', error); + setError(error.message || 'Ha ocurrido un error al obtener el ranking'); + setIsLoading(false); + }); + }; + useEffect(() => { const delayDebounceFn = setTimeout(() => { - fetch(gatewayUrl + `/getstats?user=${username}`) - .then((response) => response.json()) - .then((data) => { - setStats(data); - setIsLoading(false); - }) - .catch((error) => { - console.error("Error al obtener las preguntas:", error); - setError(error); - setIsLoading(false); - }); - }, 2000); - return () => clearTimeout(delayDebounceFn); - // eslint-disable-next-line - }, [username]); + fetchStats(); + fetchRanking(); + },2000); + + return () => clearTimeout(delayDebounceFn); + }, [username, gamemode]); const handleUsernameChange = (event) => { setUsername(event.target.value); }; + const handleGamemodeChange = (mode) => { + setGamemode(mode); + // Llama a fetchStats() para actualizar las estadísticas cuando se cambia el modo de juego + fetchStats(); + }; + + const getModeName = () => { + if(gamemode=="clasico"){ + return "Clásico"; + }else if(gamemode=="bateria"){ + return "Batería de sabios"; + } + return gamemode; + }; + if (isLoading) { return (
@@ -47,7 +85,7 @@ const Stats = () => { <>