This commit is contained in:
Lukian LEIZOUR 2024-06-02 22:03:25 +02:00
parent 7f5e401900
commit 2be1074ce5
12 changed files with 291 additions and 82 deletions

View file

@ -4,7 +4,7 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Joclud APP</title> <title>Joclud's Games</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

53
package-lock.json generated
View file

@ -8,6 +8,9 @@
"name": "front", "name": "front",
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@ -803,6 +806,51 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0" "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
} }
}, },
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz",
"integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==",
"hasInstallScript": true,
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/fontawesome-svg-core": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz",
"integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/free-solid-svg-icons": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz",
"integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==",
"hasInstallScript": true,
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.2"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/react-fontawesome": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz",
"integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==",
"dependencies": {
"prop-types": "^15.8.1"
},
"peerDependencies": {
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
"react": ">=16.3"
}
},
"node_modules/@humanwhocodes/config-array": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.14", "version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
@ -3294,7 +3342,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -3558,7 +3605,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@ -3625,8 +3671,7 @@
"node_modules/react-is": { "node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"dev": true
}, },
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.14.2", "version": "0.14.2",

View file

@ -10,6 +10,9 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "^0.2.2",
"axios": "^1.7.2", "axios": "^1.7.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",

View file

@ -1,5 +1,9 @@
import { useState } from 'react'; import { useState } from 'react';
import { BrowserRouter, Routes, Route } from "react-router-dom"; import { BrowserRouter, Routes, Route } from "react-router-dom";
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
library.add(fas)
import Home from './pages/Home'; import Home from './pages/Home';
import Login from './pages/Login'; import Login from './pages/Login';

View file

@ -1,7 +0,0 @@
.helpButton-enabled {
background-color: green;
}
.helpButton-disabled {
background-color: red;
}

View file

@ -1,10 +1,10 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import axios from 'axios'; import axios from 'axios';
import './HelpButton.css';
export default function HelpButton({ gameid, helpingprop, token }) { export default function HelpButton({ gameid, helpingprop, token }) {
const [helping, setHelping] = useState(helpingprop); const [helping, setHelping] = useState(helpingprop);
console.log("helpingprop:", helpingprop)
function addHelper() { function addHelper() {
axios.post("http://leizour.fr:3000/api/v1/games/addHelper", { token, gameid }) axios.post("http://leizour.fr:3000/api/v1/games/addHelper", { token, gameid })
@ -19,6 +19,7 @@ export default function HelpButton({ gameid, helpingprop, token }) {
} }
function handleClick(event) { function handleClick(event) {
console.log("helping:", helping)
if (helping) { if (helping) {
removeHelper(); removeHelper();
} else { } else {
@ -37,6 +38,6 @@ export default function HelpButton({ gameid, helpingprop, token }) {
}, [helping]); }, [helping]);
return ( return (
<button className="helpButton" id={`helpbutton-${gameid}`} onClick={handleClick}>Set helper</button> <button className="helpButton" id={`helpbutton-${gameid}`} onClick={handleClick}>{helping ? <FontAwesomeIcon icon="fa-solid fa-book-bookmark" /> : <FontAwesomeIcon icon="fa-solid fa-book" />}</button>
) )
} }

106
src/pages/Home.css Normal file
View file

@ -0,0 +1,106 @@
body {
text-align: center;
background-color: #66c6d6;
font-family: sans-serif;
}
.page-title {
font-size: 50px;
}
.home {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
}
.logout-button {
padding: 7px 10px;
border-radius: 15px;
border: none;
background-color: #d45555;
}
.search {
margin: 20px;
}
.search-input {
padding: 7px 10px;
border-radius: 15px;
border: none;
}
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
margin: 20px;
}
.pagination-button {
padding: 7px 10px;
border-radius: 15px;
border: none;
}
.games {
display: flex;
flex-direction: column;
margin: 20px;
gap: 20px;
}
.game {
padding: 30px;
background-color: white;
display: flex;
border-radius: 60px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.5);
justify-content: space-between;
}
.game-image {
height: 250px;
margin-left: 20px;
}
.game-right {
width: 55vw;
margin-right: 15vw;
}
.game-bottom {
align-items: center;
justify-content: center;
display: flex;
gap: 15px;
}
.helper {
background-color: #b4d666;
padding: 7px 10px;
border-radius: 15px;
}
.no-helper {
background-color: #d66666;
padding: 7px 10px;
border-radius: 15px;
}
.helpButton {
padding: 7px 10px;
border-radius: 15px;
border: none;
}
.helpButton-enabled {
background-color: #8caf39;
}
.helpButton-disabled {
background-color: #d45555;
}

View file

@ -1,7 +1,10 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import axios from "axios"; import axios from "axios";
import './Home.css';
import HelpButton from '../assets/HelpButton'; import HelpButton from '../assets/HelpButton';
export default function Home() { export default function Home() {
@ -10,6 +13,7 @@ export default function Home() {
const [token, setToken] = useState(); const [token, setToken] = useState();
const [games, setGames] = useState([]); const [games, setGames] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [userloading, setUserLoading] = useState(true);
const [name, setName] = useState(""); const [name, setName] = useState("");
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
@ -22,6 +26,7 @@ export default function Home() {
setToken(tokenLocal); setToken(tokenLocal);
setUser(JSON.parse(atob(tokenLocal.split(".")[1])).user); setUser(JSON.parse(atob(tokenLocal.split(".")[1])).user);
setUserLoading(false);
async function fetchGames() { async function fetchGames() {
setLoading(true); setLoading(true);
@ -72,53 +77,73 @@ export default function Home() {
return ( return (
<div> <div>
<div> <h1 className='page-title'>Base de données des jeux Joclud</h1>
<h1>Home</h1> <div className='home'>
<h2 className='welcome'>Bienvenue, {userloading ? "..." : user.name} !</h2>
<button onClick={() => { <button onClick={() => {
localStorage.removeItem("token"); localStorage.removeItem("token");
navigate("/login"); navigate("/login");
}}>Logout</button> }} className='logout-button'>
Me déconnecter
</button>
</div>
<div className='search'>
<input type="text" value={name} onChange={handleSearchChange} className='search-input' placeholder='Chercher un jeu'/>
</div> </div>
Recherche : <input
type="text"
value={name}
onChange={handleSearchChange}
/>
<div className="pagination"> <div className="pagination">
<button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1}> <button onClick={() => handlePageChange(1)} disabled={currentPage === 1} className="pagination-button">
Previous <FontAwesomeIcon icon="fa-solid fa-angles-left" />
</button> </button>
<span>Page {currentPage} of {totalPages}</span> <button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1} className="pagination-button">
<button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages}> <FontAwesomeIcon icon="fa-solid fa-chevron-left" />
Next </button>
<span className='pagination-text'>Page {currentPage} sur {totalPages}</span>
<button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages} className="pagination-button">
<FontAwesomeIcon icon="fa-solid fa-chevron-right" />
</button>
<button onClick={() => handlePageChange(totalPages)} disabled={currentPage === totalPages} className="pagination-button">
<FontAwesomeIcon icon="fa-solid fa-angles-right" />
</button> </button>
</div> </div>
<div className='games'>
{loading ? ( {loading ? (
<div>Loading...</div> <div>Loading...</div>
) : ( ) : (
currentGames.map((game) => ( currentGames.map((game) => (
<div className='game'> <div className='game'>
<img src={`https://www.myludo.fr/img/jeux/1/300/${getCode(Math.floor(game.id / 1000))}/${game.id}.png`} /> <img src={`https://www.myludo.fr/img/jeux/1/300/${getCode(Math.floor(game.id / 1000))}/${game.id}.png`} className='game-image'/>
<div className='game-right'>
<h1 className='game-title'>{game.title}</h1> <h1 className='game-title'>{game.title}</h1>
<div className='game-bottom'>
<div className='helpers'> <div className='helpers'>
{(JSON.parse(game.helpers).map((helper) => ( {JSON.parse(game.helpers).length === 0 ? (
<p className='no-helper'>Aucun membre ne connait les rêgles</p>
) :
(JSON.parse(game.helpers).map((helper) => (
helper === user.username ? () => { } : ( helper === user.username ? () => { } : (
<div className='helper'> <p className='helper'>{helper}</p>
<h2 className='helper-title'>{helper}</h2>
</div>
))))} ))))}
</div> </div>
<HelpButton gameid={game.id} helpingprop={JSON.parse(game.helpers).includes(user.id)} token={token} /> <HelpButton gameid={game.id} helpingprop={JSON.parse(game.helpers).includes(user.username)} token={token} />
</div>
</div>
</div> </div>
)) ))
)} )}
</div>
<div className="pagination"> <div className="pagination">
<button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1}> <button onClick={() => handlePageChange(1)} disabled={currentPage === 1} className="pagination-button">
Previous <FontAwesomeIcon icon="fa-solid fa-angles-left" />
</button> </button>
<span>Page {currentPage} of {totalPages}</span> <button onClick={() => handlePageChange(currentPage - 1)} disabled={currentPage === 1} className="pagination-button">
<button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages}> <FontAwesomeIcon icon="fa-solid fa-chevron-left" />
Next </button>
<span className='pagination-text'>Page {currentPage} sur {totalPages}</span>
<button onClick={() => handlePageChange(currentPage + 1)} disabled={currentPage === totalPages} className="pagination-button">
<FontAwesomeIcon icon="fa-solid fa-chevron-right" />
</button>
<button onClick={() => handlePageChange(totalPages)} disabled={currentPage === totalPages} className="pagination-button">
<FontAwesomeIcon icon="fa-solid fa-angles-right" />
</button> </button>
</div> </div>
</div> </div>

23
src/pages/Login.css Normal file
View file

@ -0,0 +1,23 @@
body {
text-align: center;
background-color: #66c6d6;
}
.login {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.input {
padding: 7px 10px;
border-radius: 15px;
border: none;
}
.button {
padding: 7px 10px;
border-radius: 15px;
border: none;
}

View file

@ -2,6 +2,8 @@ import { useState, useEffect } from 'react'
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import axios from 'axios'; import axios from 'axios';
import './Login.css';
export default function Login() { export default function Login() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -35,20 +37,12 @@ export default function Login() {
} }
return ( return (
<div> <div className='login'>
<h1>Login</h1> <h1>Connexion</h1>
<div> <input type="text" id="username" className='input' placeholder="Nom d'utilisateur"/>
<div> <input type="password" id="password" className='input' placeholder='Mot de passe'/>
Usename : <input type="text" id="username"/> <button onClick={login} className='button'>Me connecter</button>
</div> <Link to="/register">Créer un compte</Link>
<div>
Password : <input type="password" id="password"/>
</div>
<div>
<button onClick={login}>Login</button>
</div>
</div>
<Link to="/register">Create an account</Link>
</div> </div>
) )
} }

23
src/pages/Register.css Normal file
View file

@ -0,0 +1,23 @@
body {
text-align: center;
background-color: #66c6d6;
}
.register {
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
}
.input {
padding: 7px 10px;
border-radius: 15px;
border: none;
}
.button {
padding: 7px 10px;
border-radius: 15px;
border: none;
}

View file

@ -2,6 +2,8 @@ import { useState, useEffect } from 'react'
import { useNavigate, Link } from "react-router-dom"; import { useNavigate, Link } from "react-router-dom";
import axios from 'axios'; import axios from 'axios';
import './Register.css';
export default function Register() { export default function Register() {
const navigate = useNavigate(); const navigate = useNavigate();
@ -43,26 +45,16 @@ export default function Register() {
} }
return ( return (
<div className='register'>
<h1>Création de compte</h1>
<input type="text" id="username" placeholder="Nom d'utilisateur" className='input'/>
<input type="text" id="name" placeholder='Prénom' className='input'/>
<input type="text" id="lastname" placeholder='Nom de famille' className='input'/>
<input type="password" id="password" placeholder='Mot de passe' className='input'/>
<div> <div>
<h1>Create an account</h1> <button onClick={register} className='button'>Créer un compte</button>
<div>
<div>
Usename : <input type="text" id="username"/>
</div> </div>
<div> <Link to="/login">Me connecter avec mon compte</Link>
Name : <input type="text" id="name"/>
</div>
<div>
Last name : <input type="text" id="lastname"/>
</div>
<div>
Password : <input type="password" id="password"/>
</div>
<div>
<button onClick={register}>Create an account</button>
</div>
</div>
<Link to="/login">Login with my account</Link>
</div> </div>
) )
} }