Merge branch 'main' of github.com:Charles40130/Projet-Dev-Web-Ing1

This commit is contained in:
Charles Mendiburu 2025-04-11 19:57:27 +02:00
commit 6499123131
10 changed files with 577 additions and 206 deletions

View File

@ -30,8 +30,9 @@ public class AuthHandler {
String email = body.getString("email"); String email = body.getString("email");
String gender = body.getString("gender"); String gender = body.getString("gender");
String password = body.getString("password"); 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() context.response()
.setStatusCode(400) .setStatusCode(400)
.end(new JsonObject().put("error", "Tous les champs sont requis").encode()); .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()); String hashedPassword = BCrypt.withDefaults().hashToString(12, password.toCharArray());
databaseService.pool databaseService.pool
.preparedQuery("INSERT INTO users (name, surname, email, gender, password) VALUES (?, ?, ?, ?, ?)") .preparedQuery("INSERT INTO users (name, surname, email, gender, password, pseudo) VALUES (?, ?, ?, ?, ?, ?)")
.execute(Tuple.of(name, surname, email, gender, hashedPassword)) .execute(Tuple.of(name, surname, email, gender, hashedPassword,pseudo))
.onSuccess(result -> { .onSuccess(result -> {
context.response() context.response()
.setStatusCode(201) .setStatusCode(201)

View File

@ -56,13 +56,17 @@ public class MainVerticle extends AbstractVerticle {
router.post("/modifRangeData").handler(setWeatherData::setRangeData); router.post("/modifRangeData").handler(setWeatherData::setRangeData);
router.post("/deleteObject").handler(setObjects::deleteObject); router.post("/deleteObject").handler(setObjects::deleteObject);
router.get("/users").handler(queryUsers::getUsers); router.get("/users").handler(queryUsers::getUsers);
router.post("/user").handler(queryUsers::getUser);
router.post("/setUserPoints").handler(setUser::setUserPoints); router.post("/setUserPoints").handler(setUser::setUserPoints);
router.post("/deleteUser").handler(setUser::deleteUser); router.post("/deleteUser").handler(setUser::deleteUser);
router.post("/updateProfil").handler(setUser::updateUserProfile);
router.post("/changePassword").handler(setUser::changeUserPassword);
// Routes d'authentification // Routes d'authentification
router.post("/signup").handler(authHandler::handleSignup); router.post("/signup").handler(authHandler::handleSignup);
router.post("/login").handler(authHandler::handleLogin); router.post("/login").handler(authHandler::handleLogin);
// Création du serveur HTTP // Création du serveur HTTP
vertx.createHttpServer() vertx.createHttpServer()
.requestHandler(router) .requestHandler(router)

View File

@ -4,6 +4,7 @@ import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Row; import io.vertx.sqlclient.Row;
import io.vertx.sqlclient.Tuple;
public class QueryUsers { public class QueryUsers {
private DatabaseService databaseService; private DatabaseService databaseService;
@ -32,6 +33,7 @@ public class QueryUsers {
.put("surname", row.getString("surname")) .put("surname", row.getString("surname"))
.put("email", row.getString("email")) .put("email", row.getString("email"))
.put("gender", row.getString("gender")) .put("gender", row.getString("gender"))
.put("pseudo",row.getString("pseudo"))
.put("points", points); .put("points", points);
if (points <= 60) { if (points <= 60) {
user.put("role", "user"); user.put("role", "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());
});
}
} }

View File

@ -1,5 +1,6 @@
package com.example.starter; package com.example.starter;
import at.favre.lib.crypto.bcrypt.BCrypt;
import io.vertx.core.json.JsonObject; import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.RoutingContext; import io.vertx.ext.web.RoutingContext;
import io.vertx.sqlclient.Tuple; import io.vertx.sqlclient.Tuple;
@ -10,6 +11,7 @@ public class SetUser {
public SetUser(DatabaseService ddbs) { public SetUser(DatabaseService ddbs) {
this.databaseService = ddbs; this.databaseService = ddbs;
} }
public void updateUserPoints(Integer userId, Integer points) { public void updateUserPoints(Integer userId, Integer points) {
databaseService.pool databaseService.pool
.preparedQuery("UPDATE users SET points=points+? WHERE id=?") .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) { public void setUserPoints(RoutingContext context) {
JsonObject body = context.body().asJsonObject(); JsonObject body = context.body().asJsonObject();
if (body == null) { if (body == null) {
@ -59,6 +160,7 @@ public class SetUser {
return; return;
}); });
} }
public void deleteUser(RoutingContext context) { public void deleteUser(RoutingContext context) {
JsonObject body = context.body().asJsonObject(); JsonObject body = context.body().asJsonObject();
if (body == null) { if (body == null) {

View File

@ -9,7 +9,7 @@ import Objet from "./pages/Gestion/Objet.jsx";
import AddObject from "./pages/Gestion/AddObject.jsx"; import AddObject from "./pages/Gestion/AddObject.jsx";
import Signup from "./pages/Signup.jsx"; import Signup from "./pages/Signup.jsx";
import Login from "./pages/Login.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 Sidebar from "./pages/Admin/sidebar.jsx";
import User from "./pages/Admin/User.jsx"; import User from "./pages/Admin/User.jsx";
import Dashboard from "./pages/Admin/Dashboard.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx";

View File

@ -9,7 +9,6 @@ import {
UserPlus, UserPlus,
RadioTower, RadioTower,
Binoculars, Binoculars,
Settings,
BadgePlus, BadgePlus,
} from "lucide-react"; } from "lucide-react";
function Gestion() { function Gestion() {

View File

@ -1,7 +1,7 @@
import React, { useState } from "react"; 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 { 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 { useAuth } from "../AuthContext";
import { API_BASE_URL } from "../config"; import { API_BASE_URL } from "../config";
@ -10,6 +10,7 @@ function Login() {
email: "", email: "",
password: "", password: "",
}); });
const [error, setError] = useState("");
const { login } = useAuth(); // Utilisation du hook useAuth pour accéder à la fonction login const { login } = useAuth(); // Utilisation du hook useAuth pour accéder à la fonction login
const navigate = useNavigate(); // Initialisation de useNavigate const navigate = useNavigate(); // Initialisation de useNavigate
@ -19,43 +20,72 @@ function Login() {
...prev, ...prev,
[name]: value, [name]: value,
})); }));
// Réinitialiser l'erreur lorsque l'utilisateur commence à modifier le formulaire
if (error) setError("");
}; };
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
setError(""); // Réinitialiser les erreurs à chaque tentative
try { try {
const response = await fetch(`${API_BASE_URL}/login`, { const response = await axios.post(`${API_BASE_URL}/login`, formData, {
method: "POST",
headers: { headers: {
"Content-Type": "application/json", "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) { if (data.token) {
// Appel de la fonction login du contexte pour stocker le token
login(data.token); login(data.token);
// Rediriger vers la page d'accueil après la connexion
navigate("/"); navigate("/");
} else { } else {
console.error("Token manquant dans la réponse"); setError("Authentification échouée : token manquant dans la réponse");
} }
} catch (error) { } catch (error) {
console.error("Erreur lors de la connexion", 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 ( return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 py-12 px-4 sm:px-6 lg:px-8"> <div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="md:w-96 w-full bg-white rounded-lg shadow-md p-6 mx-auto"> <div className="md:w-96 w-full bg-white rounded-lg shadow-md p-6 mx-auto">
<h2 className="text-2xl font-bold text-gray-800 mb-6 text-center"> <h2 className="text-2xl font-bold text-gray-800 mb-6 text-center">
Connexion Connexion
</h2> </h2>
{/* Message d'erreur */}
{error && (
<div className="mb-4 p-3 bg-red-50 border border-red-200 text-red-700 rounded-md flex items-start">
<AlertCircle className="h-5 w-5 mr-2 mt-0.5 flex-shrink-0" />
<span className="text-sm">{error}</span>
</div>
)}
<form onSubmit={handleSubmit} className="space-y-4"> <form onSubmit={handleSubmit} className="space-y-4">
{/* Email */} {/* Email */}
<div> <div>
@ -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" 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 required
minLength="8" minLength="8"
autoComplete="current-password"
/> />
</div> </div>
</div> </div>

View File

@ -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 (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto">
<h1 className="text-3xl font-bold text-gray-900 mb-8 text-center">Mon Profil</h1>
{errorMessage && (
<div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4">
{errorMessage}
</div>
)}
{successMessage && (
<div className="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4">
{successMessage}
</div>
)}
<div className="grid md:grid-cols-2 gap-8">
{/* Informations du profil */}
<div className="bg-white rounded-lg shadow-md p-6">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-semibold text-gray-800">Informations Personnelles</h2>
<button
onClick={() => setEditMode(!editMode)}
className="text-indigo-600 hover:text-indigo-800"
>
{editMode ? <Save className="h-5 w-5" /> : <Edit className="h-5 w-5" />}
</button>
</div>
<form onSubmit={handleProfileSubmit} className="space-y-4">
<div className="flex items-center space-x-4 mb-4">
<div className="w-20 h-20 bg-indigo-100 rounded-full flex items-center justify-center">
<User className="h-10 w-10 text-indigo-600" />
</div>
<div>
<h3 className="text-lg font-medium">{userData.name} {userData.surname} ({userData.pseudo})</h3>
<p className="text-gray-500">{userData.email}</p>
</div>
</div>
<div className="bg-indigo-50 p-3 rounded-lg mb-4">
<p className="text-sm text-gray-700">Points de fidélité: <span className="font-semibold">{userData.points}</span> ({userData.role})</p>
</div>
{editMode ? (
<>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Prénom:</label>
<input
type="text"
name="name"
value={userData.name}
onChange={handleProfileChange}
className="block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Nom:</label>
<input
type="text"
name="surname"
value={userData.surname}
onChange={handleProfileChange}
className="block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Pseudo:</label>
<input
type="text"
name="pseudo"
value={userData.pseudo}
onChange={handleProfileChange}
className="block w-full rounded-lg border-gray-300 border p-2.5 focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Email:</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Mail className="h-5 w-5 text-gray-400" />
</div>
<input
type="email"
name="email"
value={userData.email}
disabled
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"
/>
</div>
</div>
<button
type="submit"
className="w-full flex justify-center py-2 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Sauvegarder
</button>
</>
) : (
<div className="space-y-3">
<div>
<p className="text-sm font-medium text-gray-500">Prénom</p>
<p className="mt-1">{userData.name}</p>
</div>
<div>
<p className="text-sm font-medium text-gray-500">Nom</p>
<p className="mt-1">{userData.surname}</p>
</div>
<div>
<p className="text-sm font-medium text-gray-500">Pseudo</p>
<p className="mt-1">{userData.pseudo}</p>
</div>
<div>
<p className="text-sm font-medium text-gray-500">Email</p>
<p className="mt-1">{userData.email}</p>
</div>
</div>
)}
</form>
</div>
{/* Changement de mot de passe */}
<div className="bg-white rounded-lg shadow-md p-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4">Modifier le mot de passe</h2>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mot de passe actuel:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="oldPassword"
value={formData.oldPassword}
onChange={handleChange}
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"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Nouveau mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="newPassword"
value={formData.newPassword}
onChange={handleChange}
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"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Confirmer le nouveau mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
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"
/>
</div>
</div>
<button
type="submit"
className="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Modifier le mot de passe
</button>
</form>
</div>
</div>
</div>
</div>
);
}
export default Profil;

View File

@ -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 (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="w-96 bg-white rounded-lg shadow-md p-6 mx-auto">
<h2 className="text-2xl font-bold text-gray-800 mb-6 text-center">Settings</h2>
<form onSubmit={handleSubmit} className="space-y-4">
{/* (Formulaire changement Email, Mot de passe) */}
{/* Email */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Modifier votre email:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Mail className="h-5 w-5 text-gray-400" />
</div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
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
/>
</div>
</div>
{/* Mot de passe */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Ancien mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
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"
/>
</div>
</div>
{/* nouveau mot de passe */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Nouveau mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
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"
/>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Confirmer le nouveau mot de passe:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<Lock className="h-5 w-5 text-gray-400" />
</div>
<input
type="password"
name="confirmPassword"
value={formData.confirmPassword}
onChange={handleChange}
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"
/>
</div>
</div>
{/* Bouton d'inscription */}
<div className="pt-4">
<button
type="submit"
className="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Sauvegarder
</button>
</div>
</form>
</div>
</div>
);
}
export default Settings;

View File

@ -7,6 +7,7 @@ function Signup() {
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: '', name: '',
surname: '', surname: '',
pseudo:'',
email: '', email: '',
gender: '', gender: '',
password: '', password: '',
@ -62,7 +63,7 @@ function Signup() {
{/* Formulaire (Nom, Prénom, Sexe, Email, Mot de passe) */} {/* Formulaire (Nom, Prénom, Sexe, Email, Mot de passe) */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Nom: Prénom:
</label> </label>
<div className="relative"> <div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
@ -81,7 +82,7 @@ function Signup() {
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Prénom: Nom:
</label> </label>
<div className="relative"> <div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
@ -98,6 +99,25 @@ function Signup() {
</div> </div>
</div> </div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Pseudo:
</label>
<div className="relative">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<User className="h-5 w-5 text-gray-400" />
</div>
<input
type="text"
name="pseudo"
value={formData.pseudo}
onChange={handleChange}
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
/>
</div>
</div>
{/* Sexe */} {/* Sexe */}
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">