From b3e86fc6973fc7983b27b622501a239080805a73 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Mar 2024 08:16:11 +0100 Subject: [PATCH 1/5] Divided scripts for start server-frontend --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index dba7c6e..95eef7f 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ }, "scripts": { "start-server": "node src/backend/server.js", - "start-frontend": "react-scripts start --port 3003", - "start": "npm run start-server & npm run start-frontend", + "start-frontend": "react-scripts start ", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" From f398dac9b8858d0721edcae4d9a0bfde86d664eb Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Mar 2024 09:16:39 +0100 Subject: [PATCH 2/5] Refactoring of Main Components --- src/backend/API/teams.js | 6 ++--- src/component/CreateTeam.jsx | 17 ++++++------ src/component/SubmitTeam.jsx | 30 ++++++++++++++------- src/component/TeamForm.jsx | 52 ------------------------------------ src/pages/main.jsx | 14 ++++------ 5 files changed, 37 insertions(+), 82 deletions(-) delete mode 100644 src/component/TeamForm.jsx diff --git a/src/backend/API/teams.js b/src/backend/API/teams.js index 2e314ff..4ed77aa 100644 --- a/src/backend/API/teams.js +++ b/src/backend/API/teams.js @@ -14,7 +14,7 @@ app.use(express.json()); app.get('/api/teams', async (req, res) => { try { - const teams = await db.select().from('team'); + const teams = await db.select().from('teams'); res.json(teams); } catch (error) { console.error('Error fetching teams:', error); @@ -24,9 +24,9 @@ app.get('/api/teams', async (req, res) => { app.post('/api/teams', async (req, res) => { try { - const { title, code } = req.body; // Aquí se espera que el campo se llame 'title' + const { team_name, team_code } = req.body; const creationDate = new Date(); - await db('team').insert({ title, code, created_date: creationDate }); // Insertar 'title' en lugar de 'name' + await db('teams').insert({ team_name, team_code, created_date: creationDate }); res.status(201).json({ message: 'Team created successfully' }); } catch (error) { console.error('Error creating team:', error); diff --git a/src/component/CreateTeam.jsx b/src/component/CreateTeam.jsx index b2522a6..956fb7c 100644 --- a/src/component/CreateTeam.jsx +++ b/src/component/CreateTeam.jsx @@ -1,11 +1,13 @@ -const CreateTeam = ({ setTeamsDatabase, setTeamCode }) => { +import '../index.css'; + +const CreateTeam = ({ setTeamsDatabase }) => { const handleCreateTeam = async () => { try { const randomCode = Math.random().toString(36).substring(2, 8).toUpperCase(); - const title = prompt("Enter new team name:"); + const newName = prompt("Enter new team name:"); - if (!title) { + if (!newName) { return; } @@ -14,16 +16,15 @@ const CreateTeam = ({ setTeamsDatabase, setTeamCode }) => { headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ title: title, code: randomCode }) // Cambio aquí + body: JSON.stringify({ team_name: newName, team_code: randomCode }) }); if (!response.ok) { throw new Error('Failed to create team'); } - setTeamsDatabase(prevTeams => [...prevTeams, { id: prevTeams.length + 1, title, code: randomCode }]); - setTeamCode(randomCode); - window.alert(`New team created: ${title} (Code: ${randomCode})`); + setTeamsDatabase(prevTeams => [...prevTeams, { id: prevTeams.length + 1, name: newName, code: randomCode }]); + window.alert(`New team created: ${newName} (Code: ${randomCode})`); } catch (error) { console.error('Error creating team:', error); window.alert('Failed to create team'); @@ -35,4 +36,4 @@ const CreateTeam = ({ setTeamsDatabase, setTeamCode }) => { ); }; -export default CreateTeam; +export default CreateTeam; \ No newline at end of file diff --git a/src/component/SubmitTeam.jsx b/src/component/SubmitTeam.jsx index a5809e4..075a94a 100644 --- a/src/component/SubmitTeam.jsx +++ b/src/component/SubmitTeam.jsx @@ -1,25 +1,35 @@ import { useState } from 'react'; import '../index.css'; - -const SubmitTeam = ({ teamCode, teamsDatabase }) => { +const SubmitTeam = () => { const [inputCode, setInputCode] = useState(''); const handleInputChange = (event) => { setInputCode(event.target.value); }; - const handleSubmit = () => { - const team = teamsDatabase.find((team) => team.code === inputCode); - if (team) { - window.alert(`Log in ${team.title}`); - } else { - window.alert('Invalid team code'); + const handleSubmit = async () => { + try { + const response = await fetch('http://localhost:4050/api/teams'); + if (!response.ok) { + throw new Error('Failed to fetch teams'); + } + const teams = await response.json(); + + const team = teams.find((team) => team.team_code === inputCode); + if (team) { + window.alert(`Logged in to ${team.team_name}`); + } else { + window.alert('Invalid team code'); + } + } catch (error) { + console.error('Error fetching teams:', error); + window.alert('Failed to fetch teams'); } }; return ( -
+
{ ); }; -export default SubmitTeam; +export default SubmitTeam; \ No newline at end of file diff --git a/src/component/TeamForm.jsx b/src/component/TeamForm.jsx deleted file mode 100644 index e29c5c3..0000000 --- a/src/component/TeamForm.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useState, useEffect } from 'react'; - -const TeamForm = () => { - const [teamCode, setTeamCode] = useState(''); - const [teams, setTeams] = useState([]); - - const handleInputChange = (event) => { - setTeamCode(event.target.value); - }; - - const handleSubmit = () => { - const team = teams.find(team => team.code === teamCode); - if (team) { - window.alert(`Log in ${team.name}`); - } else { - window.alert('Invalid team code'); - } - }; - - const handleCreateTeam = () => { - const randomName = `Team ${Math.floor(Math.random() * 100)}`; - const randomCode = Math.random().toString(36).substring(2, 8).toUpperCase(); - const newTeam = { id: teams.length + 1, name: randomName, code: randomCode }; - - setTeams(prevTeams => [...prevTeams, newTeam]); - - setTeamCode(randomCode); - window.alert(`New team created: ${randomName} (Code: ${randomCode})`); - - console.log('Updated teams list:', teams); - }; - - return ( -
-

Time Off

-
- - -
- - -
- ); -}; - -export default TeamForm; diff --git a/src/pages/main.jsx b/src/pages/main.jsx index c8c9e70..88719fb 100644 --- a/src/pages/main.jsx +++ b/src/pages/main.jsx @@ -4,15 +4,15 @@ import CreateTeam from '../component/CreateTeam'; import '../index.css'; const Main = () => { - const [teamCode, setTeamCode] = useState(''); const [teamsDatabase, setTeamsDatabase] = useState([]); useEffect(() => { fetchTeams(); }, []); + const fetchTeams = async () => { try { - const response = await fetch('http://localhost:4050/api/teams'); // Adjust URL as needed + const response = await fetch('http://localhost:4050/api/teams'); if (!response.ok) { throw new Error('Failed to fetch teams'); } @@ -26,14 +26,10 @@ const Main = () => { return (

Time Off

- - + +
); }; -export default Main; +export default Main; \ No newline at end of file From 2041f804c41c46bfbc0dd35ec171a194cbf8be80 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 25 Mar 2024 19:38:54 +0100 Subject: [PATCH 3/5] added first jwt authentication to login --- .env | 4 +- package-lock.json | 134 ++++++++++++++++++++++++++++++++++- package.json | 2 + src/App.js | 16 +++-- src/backend/API/login.js | 25 +++++++ src/backend/API/teams.js | 24 ++----- src/backend/server.js | 18 ++++- src/component/SubmitTeam.jsx | 41 +++++++---- src/pages/dashboard.jsx | 3 + 9 files changed, 226 insertions(+), 41 deletions(-) create mode 100644 src/backend/API/login.js diff --git a/.env b/.env index 575aaca..2a62ee5 100644 --- a/.env +++ b/.env @@ -3,4 +3,6 @@ PGDATABASE='TimeOffDB' PGUSER='TimeOffDB_owner' PGPASSWORD='wKT0JAWF6BqD' ENDPOINT_ID='ep-empty-voice-a2dpidxi' -PORT=4050 \ No newline at end of file +PORT=4050 + +JWT_SECRET=a3b720d206c82f42da48594a96089e14acb3f7d62633af6d420de25526fe47c5 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9e73fae..9b629da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,12 +14,14 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.18.3", + "jsonwebtoken": "^9.0.2", "knex": "^3.1.0", "mysql2": "^3.9.2", "pg": "^8.11.3", "postgres": "^3.4.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" } @@ -3359,6 +3361,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -5966,6 +5976,11 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7323,6 +7338,14 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -12427,6 +12450,27 @@ "node": ">=0.10.0" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -12441,6 +12485,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -12639,6 +12702,36 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -12649,6 +12742,11 @@ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -15463,6 +15561,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/react-scripts": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", @@ -17915,9 +18043,9 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.3", diff --git a/package.json b/package.json index 95eef7f..28dc594 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,14 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.18.3", + "jsonwebtoken": "^9.0.2", "knex": "^3.1.0", "mysql2": "^3.9.2", "pg": "^8.11.3", "postgres": "^3.4.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-router-dom": "^6.22.3", "react-scripts": "5.0.1", "web-vitals": "^2.1.4" }, diff --git a/src/App.js b/src/App.js index e7e8aef..b4467d4 100644 --- a/src/App.js +++ b/src/App.js @@ -1,14 +1,20 @@ +import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; import './App.css'; import Navbar from './pages/Navbar'; import Main from './pages/main'; - +import Dashboard from './pages/dashboard'; function App() { return ( -
- -
-
+ +
+ + + } /> + } /> + +
+
); } diff --git a/src/backend/API/login.js b/src/backend/API/login.js new file mode 100644 index 0000000..720a371 --- /dev/null +++ b/src/backend/API/login.js @@ -0,0 +1,25 @@ +const express = require('express'); +const jwt = require('jsonwebtoken'); +const db = require('../database'); +const router = express.Router(); + +router.post('/', async (req, res) => { + const { team_code } = req.body; + + try { + const team = await db.select().from('teams').where('team_code', team_code).first(); + + if (!team) { + return res.status(401).json({ error: 'Invalid team code' }); + } + + const token = jwt.sign({ teamId: team.id }, process.env.JWT_SECRET, { expiresIn: '1h' }); + + res.json({ token }); + } catch (error) { + console.error('Error logging in:', error); + res.status(500).json({ error: 'Internal Server Error' }); + } +}); + +module.exports = router; diff --git a/src/backend/API/teams.js b/src/backend/API/teams.js index 4ed77aa..02e2684 100644 --- a/src/backend/API/teams.js +++ b/src/backend/API/teams.js @@ -1,20 +1,10 @@ const express = require('express'); -const cors = require('cors'); const db = require('../database'); +const router = express.Router(); -const app = express(); - -app.use(cors({ - origin: 'http://localhost:4051', - methods: ['GET', 'POST'], - credentials: true -})); - -app.use(express.json()); - -app.get('/api/teams', async (req, res) => { +router.get('/', async (req, res) => { try { - const teams = await db.select().from('teams'); + const teams = await db.select().from('teams'); res.json(teams); } catch (error) { console.error('Error fetching teams:', error); @@ -22,11 +12,11 @@ app.get('/api/teams', async (req, res) => { } }); -app.post('/api/teams', async (req, res) => { +router.post('/', async (req, res) => { try { - const { team_name, team_code } = req.body; + const { team_name, team_code } = req.body; const creationDate = new Date(); - await db('teams').insert({ team_name, team_code, created_date: creationDate }); + await db('teams').insert({ team_name, team_code, created_date: creationDate }); res.status(201).json({ message: 'Team created successfully' }); } catch (error) { console.error('Error creating team:', error); @@ -34,4 +24,4 @@ app.post('/api/teams', async (req, res) => { } }); -module.exports = app; +module.exports = router; diff --git a/src/backend/server.js b/src/backend/server.js index 2a504fd..5b4bb24 100755 --- a/src/backend/server.js +++ b/src/backend/server.js @@ -1,4 +1,20 @@ -const app = require('./API/teams'); +const express = require('express'); +const cors = require('cors'); +const loginRouter = require('./API/login'); +const teamsRouter = require('./API/teams'); + +const app = express(); + +app.use(cors({ + origin: 'http://localhost:4051', + methods: ['GET', 'POST'], + credentials: true +})); + +app.use(express.json()); + +app.use('/api/login', loginRouter); +app.use('/api/teams', teamsRouter); const PORT = process.env.PORT || 3030; app.listen(PORT, () => { diff --git a/src/component/SubmitTeam.jsx b/src/component/SubmitTeam.jsx index 075a94a..1fe7ab7 100644 --- a/src/component/SubmitTeam.jsx +++ b/src/component/SubmitTeam.jsx @@ -3,28 +3,38 @@ import '../index.css'; const SubmitTeam = () => { const [inputCode, setInputCode] = useState(''); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); const handleInputChange = (event) => { setInputCode(event.target.value); }; const handleSubmit = async () => { + setLoading(true); + try { - const response = await fetch('http://localhost:4050/api/teams'); + const response = await fetch('http://localhost:4050/api/login', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ team_code: inputCode }) + }); + if (!response.ok) { - throw new Error('Failed to fetch teams'); - } - const teams = await response.json(); - - const team = teams.find((team) => team.team_code === inputCode); - if (team) { - window.alert(`Logged in to ${team.team_name}`); - } else { - window.alert('Invalid team code'); + throw new Error('Failed to authenticate team'); } + + const { token } = await response.json(); + sessionStorage.setItem('token', token); + + window.location.href = '/dashboard'; } catch (error) { - console.error('Error fetching teams:', error); - window.alert('Failed to fetch teams'); + console.error('Error authenticating team:', error); + setError('Invalid team code'); + } finally { + setLoading(false); } }; @@ -38,9 +48,12 @@ const SubmitTeam = () => { onChange={handleInputChange} placeholder=" Enter Team Code" /> - + + {error &&
{error}
}
); }; -export default SubmitTeam; \ No newline at end of file +export default SubmitTeam; diff --git a/src/pages/dashboard.jsx b/src/pages/dashboard.jsx index e69de29..2e655cc 100644 --- a/src/pages/dashboard.jsx +++ b/src/pages/dashboard.jsx @@ -0,0 +1,3 @@ +export default function dashboard(){ +

You are in the dashboard

+} \ No newline at end of file From f3e16746335b99ec15687ff6036fb9b64d217de0 Mon Sep 17 00:00:00 2001 From: Nico Date: Tue, 26 Mar 2024 06:33:16 +0100 Subject: [PATCH 4/5] Login context is added for accessing the dashboard --- src/App.js | 17 ++++++++++------- src/UserContext.js | 18 ++++++++++++++++++ src/component/SubmitTeam.jsx | 36 +++++++++++++++++++----------------- src/pages/dashboard.jsx | 20 +++++++++++++++++--- 4 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 src/UserContext.js diff --git a/src/App.js b/src/App.js index b4467d4..ea9b459 100644 --- a/src/App.js +++ b/src/App.js @@ -3,17 +3,20 @@ import './App.css'; import Navbar from './pages/Navbar'; import Main from './pages/main'; import Dashboard from './pages/dashboard'; +import { UserProvider } from './UserContext'; function App() { return ( -
- - - } /> - } /> - -
+ +
+ + + } /> + } /> + +
+
); } diff --git a/src/UserContext.js b/src/UserContext.js new file mode 100644 index 0000000..f5deccf --- /dev/null +++ b/src/UserContext.js @@ -0,0 +1,18 @@ +import { createContext, useContext, useState } from 'react'; + +const UserContext = createContext(); + +export const useUser = () => useContext(UserContext); + +export const UserProvider = ({ children }) => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + + const login = () => setIsLoggedIn(true); + const logout = () => setIsLoggedIn(false); + + return ( + + {children} + + ); +}; diff --git a/src/component/SubmitTeam.jsx b/src/component/SubmitTeam.jsx index 1fe7ab7..f9f8ee1 100644 --- a/src/component/SubmitTeam.jsx +++ b/src/component/SubmitTeam.jsx @@ -1,40 +1,42 @@ -import { useState } from 'react'; -import '../index.css'; +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useUser } from "../UserContext"; const SubmitTeam = () => { - const [inputCode, setInputCode] = useState(''); + const [inputCode, setInputCode] = useState(""); const [loading, setLoading] = useState(false); - const [error, setError] = useState(''); + const [error, setError] = useState(""); + const { login } = useUser(); + const navigate = useNavigate(); const handleInputChange = (event) => { setInputCode(event.target.value); }; const handleSubmit = async () => { - setLoading(true); + setLoading(true); try { - const response = await fetch('http://localhost:4050/api/login', { - method: 'POST', + const response = await fetch("http://localhost:4050/api/login", { + method: "POST", headers: { - 'Content-Type': 'application/json' + "Content-Type": "application/json", }, - body: JSON.stringify({ team_code: inputCode }) + body: JSON.stringify({ team_code: inputCode }), }); if (!response.ok) { - throw new Error('Failed to authenticate team'); + throw new Error("Failed to authenticate team"); } - const { token } = await response.json(); - sessionStorage.setItem('token', token); + login(); - window.location.href = '/dashboard'; + navigate("/dashboard"); } catch (error) { - console.error('Error authenticating team:', error); - setError('Invalid team code'); + console.error("Error authenticating team:", error); + setError("Invalid team code"); } finally { - setLoading(false); + setLoading(false); } }; @@ -49,7 +51,7 @@ const SubmitTeam = () => { placeholder=" Enter Team Code" /> {error &&
{error}
}
diff --git a/src/pages/dashboard.jsx b/src/pages/dashboard.jsx index 2e655cc..14b5bbb 100644 --- a/src/pages/dashboard.jsx +++ b/src/pages/dashboard.jsx @@ -1,3 +1,17 @@ -export default function dashboard(){ -

You are in the dashboard

-} \ No newline at end of file +import { useUser } from '../UserContext'; + +const Dashboard = () => { + const { isLoggedIn } = useUser(); + + return ( +
+ {isLoggedIn ? ( +

Welcome to the Dashboard!

+ ) : ( +

Please log in to access the Dashboard.

+ )} +
+ ); +}; + +export default Dashboard; From 82f18a9bfbb205a440a7800e9e47998d206cdb16 Mon Sep 17 00:00:00 2001 From: Nico Date: Thu, 28 Mar 2024 11:08:24 +0100 Subject: [PATCH 5/5] Added time session and cookies token --- package-lock.json | 9 +++++++++ package.json | 1 + src/backend/API/login.js | 3 +++ src/component/SubmitTeam.jsx | 16 +++++++++++++--- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b629da..39926b3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.18.3", + "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", "knex": "^3.1.0", "mysql2": "^3.9.2", @@ -12300,6 +12301,14 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "engines": { + "node": ">=14" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index 28dc594..63bbb65 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.18.3", + "js-cookie": "^3.0.5", "jsonwebtoken": "^9.0.2", "knex": "^3.1.0", "mysql2": "^3.9.2", diff --git a/src/backend/API/login.js b/src/backend/API/login.js index 720a371..79d913e 100644 --- a/src/backend/API/login.js +++ b/src/backend/API/login.js @@ -15,6 +15,8 @@ router.post('/', async (req, res) => { const token = jwt.sign({ teamId: team.id }, process.env.JWT_SECRET, { expiresIn: '1h' }); + res.cookie('token', token, { httpOnly: true, maxAge: 3600000 }); + res.json({ token }); } catch (error) { console.error('Error logging in:', error); @@ -22,4 +24,5 @@ router.post('/', async (req, res) => { } }); + module.exports = router; diff --git a/src/component/SubmitTeam.jsx b/src/component/SubmitTeam.jsx index f9f8ee1..172154c 100644 --- a/src/component/SubmitTeam.jsx +++ b/src/component/SubmitTeam.jsx @@ -1,13 +1,14 @@ import { useState } from "react"; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { useUser } from "../UserContext"; +import Cookies from "js-cookie"; const SubmitTeam = () => { const [inputCode, setInputCode] = useState(""); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const { login } = useUser(); - const navigate = useNavigate(); + const navigate = useNavigate(); const handleInputChange = (event) => { setInputCode(event.target.value); @@ -29,9 +30,18 @@ const SubmitTeam = () => { throw new Error("Failed to authenticate team"); } + const data = await response.json(); + const token = data.token; + + if (!token) { + throw new Error("Token not received"); + } + + Cookies.set("token", token); + login(); - navigate("/dashboard"); + navigate("/dashboard"); } catch (error) { console.error("Error authenticating team:", error); setError("Invalid team code");