diff --git a/Back-end/src/main/java/com/example/starter/AuthHandler.java b/Back-end/src/main/java/com/example/starter/AuthHandler.java index 2e62e0e..12205b3 100644 --- a/Back-end/src/main/java/com/example/starter/AuthHandler.java +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -30,8 +30,9 @@ public class AuthHandler { String email = body.getString("email"); String gender = body.getString("gender"); String password = body.getString("password"); + String pseudo = body.getString("pseudo"); - if (name == null || surname == null || email == null || gender == null || password == null) { + if (name == null || surname == null || email == null || gender == null || password == null || pseudo == null) { context.response() .setStatusCode(400) .end(new JsonObject().put("error", "Tous les champs sont requis").encode()); @@ -41,8 +42,8 @@ public class AuthHandler { String hashedPassword = BCrypt.withDefaults().hashToString(12, password.toCharArray()); databaseService.pool - .preparedQuery("INSERT INTO users (name, surname, email, gender, password) VALUES (?, ?, ?, ?, ?)") - .execute(Tuple.of(name, surname, email, gender, hashedPassword)) + .preparedQuery("INSERT INTO users (name, surname, email, gender, password, pseudo) VALUES (?, ?, ?, ?, ?, ?)") + .execute(Tuple.of(name, surname, email, gender, hashedPassword,pseudo)) .onSuccess(result -> { context.response() .setStatusCode(201) diff --git a/Back-end/src/main/java/com/example/starter/MainVerticle.java b/Back-end/src/main/java/com/example/starter/MainVerticle.java index b669774..b45f367 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -56,12 +56,16 @@ public class MainVerticle extends AbstractVerticle { router.post("/modifRangeData").handler(setWeatherData::setRangeData); router.post("/deleteObject").handler(setObjects::deleteObject); router.get("/users").handler(queryUsers::getUsers); + router.post("/user").handler(queryUsers::getUser); router.post("/setUserPoints").handler(setUser::setUserPoints); router.post("/deleteUser").handler(setUser::deleteUser); + router.post("/updateProfil").handler(setUser::updateUserProfile); + router.post("/changePassword").handler(setUser::changeUserPassword); // Routes d'authentification router.post("/signup").handler(authHandler::handleSignup); router.post("/login").handler(authHandler::handleLogin); + // Création du serveur HTTP vertx.createHttpServer() diff --git a/Back-end/src/main/java/com/example/starter/QueryUsers.java b/Back-end/src/main/java/com/example/starter/QueryUsers.java index e065c02..6305e81 100644 --- a/Back-end/src/main/java/com/example/starter/QueryUsers.java +++ b/Back-end/src/main/java/com/example/starter/QueryUsers.java @@ -4,6 +4,7 @@ import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.Tuple; public class QueryUsers { private DatabaseService databaseService; @@ -25,19 +26,20 @@ public class QueryUsers { .onSuccess(rows -> { JsonArray users = new JsonArray(); for (Row row : rows) { - int points=row.getInteger("points"); + int points = row.getInteger("points"); JsonObject user = new JsonObject() .put("id", row.getInteger("id")) .put("name", row.getString("name")) .put("surname", row.getString("surname")) .put("email", row.getString("email")) .put("gender", row.getString("gender")) - .put("points",points); - if(points<=60){ + .put("pseudo",row.getString("pseudo")) + .put("points", points); + if (points <= 60) { user.put("role", "user"); - }else if(points<=100){ + } else if (points <= 100) { user.put("role", "complexe"); - }else if(points>=200){ + } else if (points >= 200) { user.put("role", "admin"); } users.add(user); @@ -48,4 +50,56 @@ public class QueryUsers { }); } + + public void getUser(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + //Integer idUser = body.getInteger("idUser"); + Integer idUser = 4; + databaseService.pool + .preparedQuery("SELECT * FROM users WHERE id=?;") + .execute(Tuple.of(idUser)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.size() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + + Row row = rows.iterator().next(); + int points = row.getInteger("points"); + JsonObject user = new JsonObject() + .put("id", row.getInteger("id")) + .put("name", row.getString("name")) + .put("surname", row.getString("surname")) + .put("email", row.getString("email")) + .put("gender", row.getString("gender")) + .put("pseudo",row.getString("pseudo")) + .put("points", points); + + if (points <= 60) { + user.put("role", "user"); + } else if (points <= 100) { + user.put("role", "complexe"); + } else if (points >= 200) { + user.put("role", "admin"); + } + + context.response() + .putHeader("content-type", "application/json; charset=UTF-8") + .end(user.encode()); + }); + } } diff --git a/Back-end/src/main/java/com/example/starter/SetUser.java b/Back-end/src/main/java/com/example/starter/SetUser.java index 6bcd9b2..47aa996 100644 --- a/Back-end/src/main/java/com/example/starter/SetUser.java +++ b/Back-end/src/main/java/com/example/starter/SetUser.java @@ -1,5 +1,6 @@ package com.example.starter; +import at.favre.lib.crypto.bcrypt.BCrypt; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import io.vertx.sqlclient.Tuple; @@ -10,6 +11,7 @@ public class SetUser { public SetUser(DatabaseService ddbs) { this.databaseService = ddbs; } + public void updateUserPoints(Integer userId, Integer points) { databaseService.pool .preparedQuery("UPDATE users SET points=points+? WHERE id=?") @@ -25,6 +27,105 @@ public class SetUser { } }); } + + public void changeUserPassword(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + Integer id = body.getInteger("id"); + String oldPassword = body.getString("oldPassword"); + String newPassword = body.getString("newPassword"); + + databaseService.pool + .preparedQuery("SELECT password FROM users WHERE id=?") + .execute(Tuple.of(id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + + String currentPassword = rows.iterator().next().getString("password"); + BCrypt.Result verification = BCrypt.verifyer().verify(oldPassword.toCharArray(), currentPassword); + + if (!verification.verified) { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Ancien mot de passe incorrect").encode()); + return; + } + String hashedPassword = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray()); + + databaseService.pool + .preparedQuery("UPDATE users SET password=? WHERE id=?") + .execute(Tuple.of(hashedPassword, id)) + .onFailure(e -> { + System.err.println("Erreur lors de la mise à jour du mot de passe :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject() + .put("error", "Erreur lors de la mise à jour du mot de passe") + .encode()); + }) + .onSuccess(updateRows -> { + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "Le mot de passe a bien été mis à jour") + .encode()); + }); + }); + } + + public void updateUserProfile(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + Integer id = body.getInteger("id"); + String name = body.getString("name"); + String surname = body.getString("surname"); + String pseudo = body.getString("pseudo"); + + databaseService.pool + .preparedQuery("UPDATE users SET name=?, surname=?, pseudo=? WHERE id=?") + .execute(Tuple.of(name, surname,pseudo, id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() + .setStatusCode(404) + .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); + return; + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject() + .put("success", "Les informations de l'utilisateur ont bien été mises à jour") + .encode()); + return; + }); + } + public void setUserPoints(RoutingContext context) { JsonObject body = context.body().asJsonObject(); if (body == null) { @@ -59,36 +160,37 @@ public class SetUser { return; }); } - public void deleteUser(RoutingContext context){ + + public void deleteUser(RoutingContext context) { JsonObject body = context.body().asJsonObject(); - if(body== null){ + if (body == null) { context.response() - .setStatusCode(400) - .end(new JsonObject().put("error","Corps de la requête manquant").encode()); + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); return; } Integer id = body.getInteger("id"); databaseService.pool - .preparedQuery("DELETE FROM users WHERE id=?") - .execute(Tuple.of(id)) - .onFailure(e->{ - System.err.println("Erreur de récupération de la BDD :"+e.getMessage()); - context.response() - .setStatusCode(500) - .end(new JsonObject().put("error","Erreur de récupération de la BDD").encode()); - }) - .onSuccess(rows -> { - if(rows.rowCount()==0){ + .preparedQuery("DELETE FROM users WHERE id=?") + .execute(Tuple.of(id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.rowCount() == 0) { + context.response() .setStatusCode(404) .end(new JsonObject().put("error", "Utilisateur non trouvé").encode()); return; - } - context.response() - .putHeader("content-type","application/json: charset=UTF-8") - .end(new JsonObject().put("success", "L'utilisateur à bien été supprimé").encode()); - return; - }); + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'utilisateur à bien été supprimé").encode()); + return; + }); } } diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index c80fc95..ff6cf89 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -9,7 +9,7 @@ import Objet from "./pages/Gestion/Objet.jsx"; import AddObject from "./pages/Gestion/AddObject.jsx"; import Signup from "./pages/Signup.jsx"; import Login from "./pages/Login.jsx"; -import Settings from "./pages/Settings.jsx"; +import Profil from "./pages/Profil.jsx"; import Sidebar from "./pages/Admin/sidebar.jsx"; import User from "./pages/Admin/User.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx"; diff --git a/Front-end/src/pages/Gestion/Gestion.jsx b/Front-end/src/pages/Gestion/Gestion.jsx index dce1545..991b850 100644 --- a/Front-end/src/pages/Gestion/Gestion.jsx +++ b/Front-end/src/pages/Gestion/Gestion.jsx @@ -9,7 +9,6 @@ import { UserPlus, RadioTower, Binoculars, - Settings, BadgePlus, } from "lucide-react"; function Gestion() { diff --git a/Front-end/src/pages/Login.jsx b/Front-end/src/pages/Login.jsx index 7a06d01..21e6fae 100644 --- a/Front-end/src/pages/Login.jsx +++ b/Front-end/src/pages/Login.jsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; -import { Mail, Lock } from "lucide-react"; +import { Mail, Lock, AlertCircle } from "lucide-react"; import { useNavigate, Link } from "react-router-dom"; -import axios from "axios"; // Assurez-vous d'avoir axios importé +import axios from "axios"; import { useAuth } from "../AuthContext"; import { API_BASE_URL } from "../config"; @@ -10,6 +10,7 @@ function Login() { email: "", password: "", }); + const [error, setError] = useState(""); const { login } = useAuth(); // Utilisation du hook useAuth pour accéder à la fonction login const navigate = useNavigate(); // Initialisation de useNavigate @@ -19,43 +20,72 @@ function Login() { ...prev, [name]: value, })); + // Réinitialiser l'erreur lorsque l'utilisateur commence à modifier le formulaire + if (error) setError(""); }; const handleSubmit = async (e) => { e.preventDefault(); + setError(""); // Réinitialiser les erreurs à chaque tentative try { - const response = await fetch(`${API_BASE_URL}/login`, { - method: "POST", + const response = await axios.post(`${API_BASE_URL}/login`, formData, { headers: { "Content-Type": "application/json", }, - body: JSON.stringify(formData), }); + const data = response.data; - // Récupérer les données JSON de la réponse - const data = await response.json(); - - // Vérifiez que la réponse contient bien un token if (data.token) { - // Appel de la fonction login du contexte pour stocker le token login(data.token); - - // Rediriger vers la page d'accueil après la connexion navigate("/"); } else { - console.error("Token manquant dans la réponse"); + setError("Authentification échouée : token manquant dans la réponse"); } } catch (error) { console.error("Erreur lors de la connexion", error); + + if (error.response) { + // Le serveur a répondu avec un statut d'erreur (4xx, 5xx) + if (error.response.status === 401) { + setError("Email ou mot de passe incorrect"); + } else if (error.response.status === 422) { + setError("Données de formulaire invalides"); + } else if (error.response.status >= 500) { + setError("Erreur serveur. Veuillez réessayer plus tard."); + } else { + setError( + error.response.data.message || + "Une erreur s'est produite lors de la connexion" + ); + } + } else if (error.request) { + // La requête a été faite mais pas de réponse reçue + setError( + "Impossible de joindre le serveur. Vérifiez votre connexion internet." + ); + } else { + // Une erreur s'est produite lors de la configuration de la requête + setError("Une erreur s'est produite. Veuillez réessayer."); + } } }; + return (

Connexion

+ + {/* Message d'erreur */} + {error && ( +
+ + {error} +
+ )} +
{/* Email */}
@@ -94,6 +124,7 @@ function Login() { className="pl-10 block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" required minLength="8" + autoComplete="current-password" />
diff --git a/Front-end/src/pages/Profil.jsx b/Front-end/src/pages/Profil.jsx new file mode 100644 index 0000000..fb60988 --- /dev/null +++ b/Front-end/src/pages/Profil.jsx @@ -0,0 +1,321 @@ +import React, { useState, useEffect } from 'react'; +import { Mail, User, Lock, Edit, Save } from 'lucide-react'; +import { useNavigate } from 'react-router-dom'; +import { API_BASE_URL } from "../config"; +import axios from "axios"; + +function Profil() { + const [userData, setUserData] = useState({}); + const identifiant=4; + const [formData, setFormData] = useState({ + oldPassword: '', + newPassword: '', + confirmPassword: '' + }); + + const [editMode, setEditMode] = useState(false); + const [errorMessage, setErrorMessage] = useState(''); + const [successMessage, setSuccessMessage] = useState(''); + const navigate = useNavigate(); + + useEffect(() => { + axios + .post(`${API_BASE_URL}/user`, { + id: identifiant, + }) + .then((response) => { + setUserData(response.data); + console.log("Infos récupérées :", response.data); + }) + .catch((error) => { + console.error("Erreur lors de la récupération :", error); + }); + }, []); + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleProfileChange = (e) => { + const { name, value } = e.target; + setUserData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setErrorMessage(''); + setSuccessMessage(''); + + if (formData.newPassword !== formData.confirmPassword) { + setErrorMessage("Les nouveaux mots de passe ne correspondent pas !"); + return; + } + + try { + axios + .post(`${API_BASE_URL}/changePassword`, { + id: userData.id, // Il faudrait s'assurer que userData contient l'ID de l'utilisateur + oldPassword: formData.oldPassword, + newPassword: formData.newPassword + }) + .then((response) => { + console.log("Modification du mot de passe réussie :", response.data); + setSuccessMessage("Mot de passe modifié avec succès !"); + setFormData({ + oldPassword: '', + newPassword: '', + confirmPassword: '' + }); + }) + .catch((error) => { + console.error("Erreur lors de la modification du mot de passe :", error); + setErrorMessage(error.response?.data?.error || "Une erreur est survenue"); + }); + + setSuccessMessage("Mot de passe modifié avec succès !"); + setFormData({ + oldPassword: '', + newPassword: '', + confirmPassword: '' + }); + } catch (error) { + setErrorMessage(error.message || "Une erreur est survenue"); + } + }; + + const handleProfileSubmit = async (e) => { + e.preventDefault(); + setErrorMessage(''); + setSuccessMessage(''); + + axios + .post(`${API_BASE_URL}/updateProfil`, { + id: userData.id, + name: userData.name, + surname: userData.surname, + pseudo:userData.pseudo, + email: userData.email + }) + + .catch((error) => { + console.error("Erreur lors de la mise à jour du profil :", error); + setErrorMessage(error.response?.data?.error || "Une erreur est survenue"); + }) + .then((response) => { + console.log("Mise à jour du profil réussie :", response.data); + setSuccessMessage("Profil mis à jour avec succès !"); + setEditMode(false); + }); + }; + + return ( +
+
+

Mon Profil

+ + {errorMessage && ( +
+ {errorMessage} +
+ )} + + {successMessage && ( +
+ {successMessage} +
+ )} + +
+ {/* Informations du profil */} +
+
+

Informations Personnelles

+ +
+ + +
+
+ +
+
+

{userData.name} {userData.surname} ({userData.pseudo})

+

{userData.email}

+
+
+ +
+

Points de fidélité: {userData.points} ({userData.role})

+
+ + {editMode ? ( + <> +
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+
+ +
+ +
+
+ + + + ) : ( +
+
+

Prénom

+

{userData.name}

+
+
+

Nom

+

{userData.surname}

+
+
+

Pseudo

+

{userData.pseudo}

+
+
+

Email

+

{userData.email}

+
+
+ )} + +
+ + {/* Changement de mot de passe */} +
+

Modifier le mot de passe

+
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ + +
+
+
+
+
+ ); +} + +export default Profil; \ No newline at end of file diff --git a/Front-end/src/pages/Settings.jsx b/Front-end/src/pages/Settings.jsx deleted file mode 100644 index 924a6b4..0000000 --- a/Front-end/src/pages/Settings.jsx +++ /dev/null @@ -1,161 +0,0 @@ -import React, { useState } from 'react'; -import { Mail, User, Lock } from 'lucide-react'; -import { useNavigate, Link} from 'react-router-dom'; // Importation du hook useNavigate - -function Settings() { - const [formData, setFormData] = useState({ - name: '', - surname: '', - email: '', - gender: '', - password: '', - confirmPassword: '' - }); - const navigate = useNavigate(); // Initialisation de useNavigate - - const handleChange = (e) => { - const { name, value } = e.target; - setFormData(prev => ({ - ...prev, - [name]: value - })); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - - if (formData.password !== formData.confirmPassword) { - alert("Les mots de passe ne correspondent pas !"); - return; - } - - try { - const response = await fetch(`${API_BASE_URL}/settings`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(formData), - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.error || "Erreur lors de la modification"); - } - - alert("Modification réussie !"); - - // Redirection vers la page d'accueil après une inscription réussie - navigate("/home"); // Remplace "/home" par l'URL de ta page d'accueil - } catch (error) { - alert(error.message); - } - }; - - return ( -
-
-

Settings

-
- {/* (Formulaire changement Email, Mot de passe) */} - - - - {/* Email */} -
- -
-
- -
- -
-
- - {/* Mot de passe */} -
- -
-
- -
- -
-
- - {/* nouveau mot de passe */} -
- -
-
- -
- -
-
-
- -
-
- -
- -
-
- {/* Bouton d'inscription */} -
- -
-
- -
-
- ); -} - -export default Settings; diff --git a/Front-end/src/pages/Signup.jsx b/Front-end/src/pages/Signup.jsx index e18140f..c6703db 100644 --- a/Front-end/src/pages/Signup.jsx +++ b/Front-end/src/pages/Signup.jsx @@ -7,6 +7,7 @@ function Signup() { const [formData, setFormData] = useState({ name: '', surname: '', + pseudo:'', email: '', gender: '', password: '', @@ -62,7 +63,7 @@ function Signup() { {/* Formulaire (Nom, Prénom, Sexe, Email, Mot de passe) */}
@@ -81,7 +82,7 @@ function Signup() {
@@ -98,6 +99,25 @@ function Signup() {
+
+ +
+
+ +
+ +
+
+ {/* Sexe */}