merge conflict réglés
This commit is contained in:
parent
16fd7fe9ff
commit
5fda9e9dac
@ -5,6 +5,7 @@ import io.vertx.ext.web.RoutingContext;
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
import io.vertx.ext.auth.jwt.JWTAuth;
|
||||
import io.vertx.sqlclient.Tuple;
|
||||
import io.vertx.sqlclient.Row;
|
||||
|
||||
public class AuthHandler {
|
||||
private final DatabaseService databaseService;
|
||||
@ -78,7 +79,7 @@ public class AuthHandler {
|
||||
}
|
||||
|
||||
databaseService.pool
|
||||
.preparedQuery("SELECT id,name, surname, password, points FROM users WHERE email = ?") // Ajout de name et surname
|
||||
.preparedQuery("SELECT id, name, surname, password, points FROM users WHERE email = ?")
|
||||
.execute(Tuple.of(email))
|
||||
.onSuccess(result -> {
|
||||
if (result.rowCount() == 0) {
|
||||
@ -88,29 +89,64 @@ public class AuthHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
var row = result.iterator().next();
|
||||
// Récupération de la ligne de manière sécurisée
|
||||
Row row = null;
|
||||
try {
|
||||
row = result.iterator().next();
|
||||
} catch (Exception e) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur lors de la récupération des données utilisateur").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Vérification que row n'est pas null avant d'y accéder
|
||||
if (row == null) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Données utilisateur introuvables").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Récupération des champs de manière sécurisée
|
||||
Integer id = row.getInteger("id");
|
||||
String storedHashedPassword = row.getString("password");
|
||||
Integer nbPointsUser = row.getInteger("points");
|
||||
String name = row.getString("name");
|
||||
String surname = row.getString("surname");
|
||||
|
||||
// Vérification des champs obligatoires
|
||||
if (id == null || storedHashedPassword == null) {
|
||||
context.response()
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Données utilisateur incomplètes").encode());
|
||||
return;
|
||||
}
|
||||
|
||||
// Valeur par défaut pour les points si null
|
||||
if (nbPointsUser == null) {
|
||||
nbPointsUser = 0;
|
||||
}
|
||||
|
||||
BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword);
|
||||
|
||||
if (verification.verified) {
|
||||
JsonObject claims = new JsonObject()
|
||||
.put("sub", email)
|
||||
.put("name", name)
|
||||
.put("surname", surname)
|
||||
.put("id", id);
|
||||
|
||||
if (nbPointsUser <= 60) {
|
||||
claims.put("role", "user");
|
||||
} else if (nbPointsUser <= 100) {
|
||||
claims.put("role", "complexe");
|
||||
} else if (nbPointsUser >= 200) {
|
||||
claims.put("role", "admin");
|
||||
// Ajout de name et surname seulement s'ils ne sont pas null
|
||||
if (name != null) claims.put("name", name);
|
||||
if (surname != null) claims.put("surname", surname);
|
||||
|
||||
// Attribution du rôle en fonction des points
|
||||
String role = "user"; // Rôle par défaut
|
||||
if (nbPointsUser >= 200) {
|
||||
role = "admin";
|
||||
} else if (nbPointsUser >= 100) {
|
||||
role = "complexe";
|
||||
}
|
||||
claims.put("role", role);
|
||||
|
||||
String token = jwtAuth.generateToken(claims);
|
||||
|
||||
@ -129,4 +165,5 @@ public class AuthHandler {
|
||||
.setStatusCode(500)
|
||||
.end(new JsonObject().put("error", "Erreur serveur").encode());
|
||||
});
|
||||
}}
|
||||
}
|
||||
}
|
||||
@ -6,6 +6,12 @@ import { useAuth } from "../AuthContext";
|
||||
function Header() {
|
||||
const { token, user, logout } = useAuth();
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [showAdminDropdown, setShowAdminDropdown] = useState(false);
|
||||
|
||||
// La fonction toggleAdminDropdown permet d'ouvrir/fermer le menu déroulant
|
||||
const toggleAdminDropdown = () => {
|
||||
setShowAdminDropdown((prev) => !prev);
|
||||
};
|
||||
|
||||
return (
|
||||
<header className="bg-white shadow-md ">
|
||||
@ -90,17 +96,48 @@ function Header() {
|
||||
</Link>
|
||||
</li>
|
||||
)}
|
||||
{(user?.role === "admin")&&(
|
||||
<li>
|
||||
{user?.role === "admin" && (
|
||||
<li className="relative">
|
||||
<button
|
||||
onClick={toggleAdminDropdown}
|
||||
className="flex items-center text-gray-600 hover:text-indigo-600 focus:outline-none"
|
||||
>
|
||||
Admin
|
||||
<svg
|
||||
className="ml-1 h-4 w-4 fill-current"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" />
|
||||
</svg>
|
||||
</button>
|
||||
{showAdminDropdown && (
|
||||
<div className="absolute top-full left-0 mt-2 w-48 bg-white border border-gray-200 rounded-md shadow-lg z-50">
|
||||
<Link
|
||||
to="/dashboard"
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="text-gray-600 hover:text-indigo-600"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => setShowAdminDropdown(false)}
|
||||
>
|
||||
Administration
|
||||
Dashboard
|
||||
</Link>
|
||||
<Link
|
||||
to="/user"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => setShowAdminDropdown(false)}
|
||||
>
|
||||
Gestion des Utilisateurs
|
||||
</Link>
|
||||
<Link
|
||||
to="/adminobjet"
|
||||
className="block px-4 py-2 text-gray-700 hover:bg-gray-100"
|
||||
onClick={() => setShowAdminDropdown(false)}
|
||||
>
|
||||
Gestion des Objets Connectés
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
)}
|
||||
|
||||
<li className="sm:hidden">
|
||||
<Link
|
||||
to="/profil"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useState } from "react";
|
||||
import React, { useState,useEffect } from "react";
|
||||
import Sidebar from "./sidebar.jsx";
|
||||
import { RadioTower, ArrowRight, BadgePlus, Settings } from "lucide-react";
|
||||
import { API_BASE_URL } from "../../config.js";
|
||||
@ -13,34 +13,7 @@ const initialWidgets = [
|
||||
|
||||
function Dashboard() {
|
||||
// États simulés (données fictives pour l'exemple)
|
||||
const [users, setUsers] = useState([
|
||||
{
|
||||
id: 1,
|
||||
username: "Alice",
|
||||
email: "alice@example.com",
|
||||
accessLevel: "Admin",
|
||||
},
|
||||
{ id: 2, username: "Bob", email: "bob@example.com", accessLevel: "User" },
|
||||
{
|
||||
id: 3,
|
||||
username: "Charlie",
|
||||
email: "charlie@example.com",
|
||||
accessLevel: "Guest",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
username: "David",
|
||||
email: "david@example.com",
|
||||
accessLevel: "User",
|
||||
},
|
||||
{ id: 5, username: "Eva", email: "eva@example.com", accessLevel: "User" },
|
||||
{
|
||||
id: 6,
|
||||
username: "Frank",
|
||||
email: "frank@example.com",
|
||||
accessLevel: "Admin",
|
||||
},
|
||||
]);
|
||||
const [users, setUsers] = useState([])
|
||||
|
||||
const [logs, setLogs] = useState([
|
||||
{
|
||||
@ -56,7 +29,11 @@ function Dashboard() {
|
||||
timestamp: new Date().toLocaleString(),
|
||||
},
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
||||
setUsers(response.data);
|
||||
});
|
||||
}, []);
|
||||
// État pour simuler les objets présents dans la section AdminObjet.jsx
|
||||
const [adminObjects, setAdminObjects] = useState([
|
||||
{
|
||||
@ -111,13 +88,7 @@ function Dashboard() {
|
||||
setWidgets([...widgets, newWidget]);
|
||||
setShowAddWidgetModal(false);
|
||||
};
|
||||
/*
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
||||
setUsers(response.data);
|
||||
});
|
||||
}, []);
|
||||
*/
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen">
|
||||
<Sidebar />
|
||||
@ -200,13 +171,13 @@ function Dashboard() {
|
||||
{users.slice(0, 5).map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td className="px-2 py-1 border border-gray-200">
|
||||
{user.username}
|
||||
{user.pseudo}
|
||||
</td>
|
||||
<td className="px-2 py-1 border border-gray-200">
|
||||
{user.email}
|
||||
</td>
|
||||
<td className="px-2 py-1 border border-gray-200">
|
||||
{user.accessLevel}
|
||||
{user.role}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@ -3,45 +3,35 @@ import Sidebar from "./sidebar.jsx";
|
||||
import { API_BASE_URL } from "../../config.js";
|
||||
import axios from "axios";
|
||||
|
||||
// Définition de styles utilisés (si nécessaire – vous pouvez convertir ces styles en classes Tailwind)
|
||||
const thTd = "p-2 border border-gray-300 text-left";
|
||||
const th = `${thTd} bg-gray-100`;
|
||||
|
||||
function User() {
|
||||
const [users, setUsers] = useState([]);
|
||||
const [logs, setLogs] = useState([]);
|
||||
const [name, setName] = useState("");
|
||||
const [name, setname] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [pointsInput, setPointsInput] = useState({});
|
||||
|
||||
// Ajout d'un utilisateur
|
||||
const handleAddUser = (e) => {
|
||||
e.preventDefault();
|
||||
const newUser = {
|
||||
id: Date.now(), // ID généré temporairement ; en production, il sera géré par la BDD.
|
||||
id: Date.now(),
|
||||
name,
|
||||
email,
|
||||
accessLevel: "User",
|
||||
points: 0,
|
||||
};
|
||||
setUsers([...users, newUser]);
|
||||
logAction(name, "User added");
|
||||
setName("");
|
||||
logAction(name, "Utilisateur ajouté");
|
||||
setname("");
|
||||
setEmail("");
|
||||
// TODO : Envoyer newUser à l'API si nécessaire.
|
||||
};
|
||||
|
||||
// Chargement des utilisateurs depuis l'API au montage du composant
|
||||
useEffect(() => {
|
||||
axios
|
||||
.get(`${API_BASE_URL}/users`)
|
||||
.then((response) => setUsers(response.data))
|
||||
.catch((error) =>
|
||||
console.error("Erreur lors de la récupération des utilisateurs:", error)
|
||||
);
|
||||
axios.get(`${API_BASE_URL}/users`).then((response) => {
|
||||
setUsers(response.data);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Suppression d'un utilisateur
|
||||
const handleDeleteUser = (userId) => {
|
||||
const user = users.find((u) => u.id === userId);
|
||||
|
||||
@ -75,7 +65,6 @@ function User() {
|
||||
}
|
||||
};
|
||||
|
||||
// Changement du niveau d'accès
|
||||
const handleChangeAccessLevel = (userId, newLevel) => {
|
||||
setUsers(
|
||||
users.map((user) => {
|
||||
@ -83,9 +72,11 @@ function User() {
|
||||
const oldLevel = user.role;
|
||||
user.role = newLevel;
|
||||
if (user.role === "user") {
|
||||
user.points = 60;
|
||||
} else if (newLevel === "admin") {
|
||||
user.points = 0;
|
||||
} else if (user.role === "complexe") {
|
||||
user.points = 100;
|
||||
} else if (user.role === "admin") {
|
||||
user.points = 200;
|
||||
}
|
||||
axios
|
||||
.post(`${API_BASE_URL}/setUserPoints`, {
|
||||
@ -94,42 +85,42 @@ function User() {
|
||||
})
|
||||
.then((response) => {
|
||||
alert("Le changement de niveau a bien été enregistré !");
|
||||
console.log("Niveau changé:", response.data);
|
||||
console.log("Changement de niveau réussit :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert("Erreur lors du changement de niveau !");
|
||||
console.error(error);
|
||||
alert("Il y a eu une erreur dans le changement de niveau !");
|
||||
console.error("Erreur lors du changement de niveau :", error);
|
||||
});
|
||||
logAction(user.name, `Access level changed from ${oldLevel} to ${newLevel}`);
|
||||
logAction(
|
||||
user.name,
|
||||
`Niveau d'accés changé de ${oldLevel} à ${newLevel}`
|
||||
);
|
||||
}
|
||||
return user;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
// Ajustement des points : additionne les points saisis aux points existants
|
||||
const handleAdjustPoints = (userId) => {
|
||||
const pointsToAdd = parseInt(pointsInput[userId]) || 0;
|
||||
setUsers(
|
||||
users.map((user) => {
|
||||
if (user.id === userId) {
|
||||
user.points = pointsToAdd;
|
||||
// On additionne au lieu de remplacer
|
||||
user.points = (user.points || 0) + pointsToAdd;
|
||||
axios
|
||||
.post(`${API_BASE_URL}/setUserPoints`, {
|
||||
id: user.id,
|
||||
points: user.points,
|
||||
})
|
||||
.then((response) => {
|
||||
alert("Les points ont bien été enregistrés !");
|
||||
console.log("Points mis à jour :", response.data);
|
||||
alert("Les points ont bien été enregistré !");
|
||||
console.log("Ajout des points réussit :", response.data);
|
||||
})
|
||||
.catch((error) => {
|
||||
alert("Erreur lors de l'ajustement des points !");
|
||||
console.error(error);
|
||||
alert("Il y a eu une erreur dans l'ajout des points!");
|
||||
console.error("Erreur lors de l'ajout des points :", error);
|
||||
});
|
||||
logAction(user.name, `Points adjusted by ${pointsToAdd}, new total: ${user.points}`);
|
||||
logAction(user.name, `Points ajustés par ${pointsToAdd}`);
|
||||
}
|
||||
return user;
|
||||
})
|
||||
@ -137,121 +128,107 @@ function User() {
|
||||
setPointsInput({ ...pointsInput, [userId]: "" });
|
||||
};
|
||||
|
||||
// Fonction de journalisation des actions
|
||||
const logAction = (name, action) => {
|
||||
/*TODO*/
|
||||
/*Ajouter le suivi dans un journal de log*/
|
||||
const timestamp = new Date().toLocaleString();
|
||||
setLogs([...logs, { id: Date.now(), name, action, timestamp }]);
|
||||
};
|
||||
|
||||
// Fonction pour générer et télécharger les logs dans un fichier texte
|
||||
const downloadLogs = () => {
|
||||
const logText = logs
|
||||
.map((log) => `${log.timestamp} - ${log.name} - ${log.action}`)
|
||||
.join("\n");
|
||||
const blob = new Blob([logText], { type: "text/plain;charset=utf-8" });
|
||||
const link = document.createElement("a");
|
||||
link.href = URL.createObjectURL(blob);
|
||||
link.download = "logs.txt";
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<div className="flex min-h-screen">
|
||||
<Sidebar />
|
||||
<main className="flex-grow p-5">
|
||||
<section className="mt-5">
|
||||
<h1 className="text-2xl font-bold mb-4">Gestion des utilisateurs</h1>
|
||||
<p className="mb-5">Gérez les utilisateurs à partir de ce panneau.</p>
|
||||
<h1 className="text-2xl font-bold text-gray-900 mb-5">
|
||||
User Management
|
||||
</h1>
|
||||
<p>Manage users from this panel.</p>
|
||||
{/* Formulaire d'ajout d'utilisateur */}
|
||||
<form
|
||||
className="grid grid-cols-[1fr_1fr_auto] gap-2 mb-5"
|
||||
className="gap-3 mb-5 grid grid-cols-[1fr_1fr_auto]"
|
||||
onSubmit={handleAddUser}
|
||||
>
|
||||
<input
|
||||
className="p-3 border rounded-md"
|
||||
type="text"
|
||||
id="name"
|
||||
placeholder="Name"
|
||||
placeholder="name"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
onChange={(e) => setname(e.target.value)}
|
||||
required
|
||||
className="p-2 border border-gray-300 rounded-md"
|
||||
/>
|
||||
<input
|
||||
className="p-3 border rounded-md"
|
||||
type="email"
|
||||
id="email"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="p-2 border border-gray-300 rounded-md"
|
||||
/>
|
||||
<button
|
||||
className="p-3 bg-green-600 text-white border-none rounded-md "
|
||||
type="submit"
|
||||
className="p-2 bg-green-600 text-white rounded-md cursor-pointer"
|
||||
>
|
||||
Add User
|
||||
Ajouter utilisateur
|
||||
</button>
|
||||
</form>
|
||||
{/* Tableau des utilisateurs */}
|
||||
<table className="w-full border-collapse mb-6">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={th}>Nom</th>
|
||||
<th className={th}>Email</th>
|
||||
<th className={th}>Niveau d'accès</th>
|
||||
<th className={th}>Points</th>
|
||||
<th className={th}>Actions</th>
|
||||
<th className={`${th}`}>Nom</th>
|
||||
<th className={`${th}`}>Email</th>
|
||||
<th className={`${th}`}>Niveau d'accès</th>
|
||||
<th className={`${th}`}>Points</th>
|
||||
<th className={`${th}`}>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{users.map((user) => (
|
||||
<tr key={user.id}>
|
||||
<td className={thTd}>{user.name}</td>
|
||||
<td className={thTd}>{user.email}</td>
|
||||
<td className={thTd}>
|
||||
<td className={`${thTd}`}>{user.name}</td>
|
||||
<td className={`${thTd}`}>{user.email}</td>
|
||||
<td className={`${thTd}`}>
|
||||
<select
|
||||
value={user.accessLevel}
|
||||
value={user.role}
|
||||
onChange={(e) =>
|
||||
handleChangeAccessLevel(user.id, e.target.value)
|
||||
}
|
||||
className="p-1 rounded-md border border-gray-300"
|
||||
className="p-2 rounded-md"
|
||||
>
|
||||
<option value="admin">Admin</option>
|
||||
<option value="user">User</option>
|
||||
<option value="complexe">Complexe</option>
|
||||
</select>
|
||||
</td>
|
||||
<td className={thTd}>
|
||||
<td className={`${thTd}`}>
|
||||
<input
|
||||
className="border ml-4 w-16"
|
||||
type="number"
|
||||
min="0"
|
||||
value={pointsInput[user.id] ?? user.points}
|
||||
value={pointsInput[user.id] || user.points}
|
||||
onChange={(e) =>
|
||||
setPointsInput({
|
||||
...pointsInput,
|
||||
[user.id]: e.target.value,
|
||||
})
|
||||
}
|
||||
className="w-16 ml-2 p-1 border border-gray-300 rounded-md"
|
||||
/>
|
||||
<button
|
||||
className="p-2 bg-green-600 text-white border-none rounded-md"
|
||||
onClick={() => handleAdjustPoints(user.id)}
|
||||
className="p-2 bg-green-600 text-white rounded-md ml-2"
|
||||
>
|
||||
Changer
|
||||
Changer
|
||||
</button>
|
||||
</td>
|
||||
<td className={thTd}>
|
||||
<td className={`${thTd}`}>
|
||||
<button
|
||||
className="p-2 bg-red-600 text-white border-none rounded-md"
|
||||
onClick={() => handleDeleteUser(user.id)}
|
||||
className="p-2 bg-red-600 text-white rounded-md"
|
||||
>
|
||||
Supprimer
|
||||
Supprimer
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
@ -260,34 +237,28 @@ function User() {
|
||||
</table>
|
||||
</section>
|
||||
{/* Tableau des logs */}
|
||||
<section className="mt-10">
|
||||
<h2 className="text-2xl font-bold mb-4">
|
||||
Historique des connexions et journal des logs
|
||||
<section className="user-logs mt-10">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-5">
|
||||
Historique des connexions et journal des logs{" "}
|
||||
</h2>
|
||||
<table className="w-full border-collapse mb-4">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={th}>Nom</th>
|
||||
<th className={th}>Action</th>
|
||||
<th className={th}>Timestamp</th>
|
||||
<th className={`${th}`}>Nom</th>
|
||||
<th className={`${th}`}>Action</th>
|
||||
<th className={`${th}`}>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{logs.map((log) => (
|
||||
<tr key={log.id}>
|
||||
<td className={thTd}>{log.name}</td>
|
||||
<td className={thTd}>{log.action}</td>
|
||||
<td className={thTd}>{log.timestamp}</td>
|
||||
<td className={`${thTd}`}>{log.name}</td>
|
||||
<td className={`${thTd}`}>{log.action}</td>
|
||||
<td className={`${thTd}`}>{log.timestamp}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<button
|
||||
onClick={downloadLogs}
|
||||
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-md"
|
||||
>
|
||||
Télécharger les logs
|
||||
</button>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useState } from "react";
|
||||
import { Mail, Lock, AlertCircle } from "lucide-react";
|
||||
import { Mail, Lock, AlertCircle, CheckCircle, Info, X } from "lucide-react";
|
||||
import { useNavigate, Link } from "react-router-dom";
|
||||
import axios from "axios";
|
||||
import { useAuth } from "../AuthContext";
|
||||
@ -10,7 +10,11 @@ function Login() {
|
||||
email: "",
|
||||
password: "",
|
||||
});
|
||||
const [error, setError] = useState("");
|
||||
const [alert, setAlert] = useState({
|
||||
show: false,
|
||||
type: "", // 'success', 'error', 'info', 'warning'
|
||||
message: "",
|
||||
});
|
||||
const { login } = useAuth();
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -20,14 +24,32 @@ function Login() {
|
||||
...prev,
|
||||
[name]: value,
|
||||
}));
|
||||
if (error) setError("");
|
||||
if (alert.show) setAlert({ ...alert, show: false });
|
||||
};
|
||||
|
||||
const showAlert = (type, message) => {
|
||||
setAlert({
|
||||
show: true,
|
||||
type,
|
||||
message,
|
||||
});
|
||||
|
||||
// Auto-hide success and info alerts after 5 seconds
|
||||
if (type === 'success' || type === 'info') {
|
||||
setTimeout(() => {
|
||||
setAlert(prev => ({ ...prev, show: false }));
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setError("");
|
||||
setAlert({ show: false, type: "", message: "" });
|
||||
|
||||
try {
|
||||
// Afficher un message de chargement
|
||||
showAlert("info", "Connexion en cours...");
|
||||
|
||||
const response = await axios.post(`${API_BASE_URL}/login`, formData, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@ -36,37 +58,65 @@ function Login() {
|
||||
const data = response.data;
|
||||
|
||||
if (data.token) {
|
||||
showAlert("success", "Connexion réussie! Redirection...");
|
||||
login(data.token);
|
||||
|
||||
// Court délai pour montrer le message de succès avant la redirection
|
||||
setTimeout(() => {
|
||||
navigate("/");
|
||||
}, 1000);
|
||||
} else {
|
||||
setError("Authentification échouée : token manquant dans la réponse");
|
||||
showAlert("error", "Authentification échouée : token manquant dans la réponse");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur lors de la connexion", error);
|
||||
|
||||
if (error.response) {
|
||||
if (error.response.status === 401) {
|
||||
setError("Email ou mot de passe incorrect");
|
||||
showAlert("error", "Email ou mot de passe incorrect");
|
||||
} else if (error.response.status === 422) {
|
||||
setError("Données de formulaire invalides");
|
||||
showAlert("error", "Données de formulaire invalides");
|
||||
} else if (error.response.status >= 500) {
|
||||
setError("Erreur serveur. Veuillez réessayer plus tard.");
|
||||
showAlert("error", "Erreur serveur. Veuillez réessayer plus tard.");
|
||||
} else {
|
||||
setError(
|
||||
error.response.data.message ||
|
||||
"Une erreur s'est produite lors de la connexion"
|
||||
);
|
||||
showAlert("error", error.response.data.message || "Une erreur s'est produite lors de la connexion");
|
||||
}
|
||||
} else if (error.request) {
|
||||
setError(
|
||||
"Impossible de joindre le serveur. Vérifiez votre connexion internet."
|
||||
);
|
||||
showAlert("error", "Impossible de joindre le serveur. Vérifiez votre connexion internet.");
|
||||
} else {
|
||||
setError("Une erreur s'est produite. Veuillez réessayer.");
|
||||
showAlert("error", "Une erreur s'est produite. Veuillez réessayer.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Configuration des alertes selon le type
|
||||
const alertConfig = {
|
||||
success: {
|
||||
bgColor: "bg-green-50",
|
||||
borderColor: "border-green-200",
|
||||
textColor: "text-green-700",
|
||||
icon: <CheckCircle className="h-5 w-5 mr-2 mt-0.5 flex-shrink-0 text-green-500" />
|
||||
},
|
||||
error: {
|
||||
bgColor: "bg-red-50",
|
||||
borderColor: "border-red-200",
|
||||
textColor: "text-red-700",
|
||||
icon: <AlertCircle className="h-5 w-5 mr-2 mt-0.5 flex-shrink-0 text-red-500" />
|
||||
},
|
||||
info: {
|
||||
bgColor: "bg-blue-50",
|
||||
borderColor: "border-blue-200",
|
||||
textColor: "text-blue-700",
|
||||
icon: <Info className="h-5 w-5 mr-2 mt-0.5 flex-shrink-0 text-blue-500" />
|
||||
},
|
||||
warning: {
|
||||
bgColor: "bg-yellow-50",
|
||||
borderColor: "border-yellow-200",
|
||||
textColor: "text-yellow-700",
|
||||
icon: <AlertCircle className="h-5 w-5 mr-2 mt-0.5 flex-shrink-0 text-yellow-500" />
|
||||
}
|
||||
};
|
||||
|
||||
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="md:w-96 w-full bg-white rounded-lg shadow-md p-6 mx-auto">
|
||||
@ -74,11 +124,19 @@ function Login() {
|
||||
Connexion
|
||||
</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>
|
||||
{/* Système d'alertes */}
|
||||
{alert.show && (
|
||||
<div className={`mb-4 p-3 ${alertConfig[alert.type].bgColor} border ${alertConfig[alert.type].borderColor} ${alertConfig[alert.type].textColor} rounded-md flex items-start justify-between`}>
|
||||
<div className="flex items-start">
|
||||
{alertConfig[alert.type].icon}
|
||||
<span className="text-sm">{alert.message}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setAlert({ ...alert, show: false })}
|
||||
className="ml-2 p-1 hover:bg-gray-200 rounded-full"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user