Ajout du backend sur les users

This commit is contained in:
Mathis 2025-04-10 22:08:41 +02:00
parent 68917bbcb0
commit b10408f443
4 changed files with 188 additions and 200 deletions

View File

@ -54,6 +54,8 @@ public class MainVerticle extends AbstractVerticle {
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("/setUserPoints").handler(setUser::setUserPoints); router.post("/setUserPoints").handler(setUser::setUserPoints);
router.post("/deleteUser").handler(setUser::deleteUser);
// 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);

View File

@ -25,7 +25,7 @@ public class SetUser {
databaseService.pool databaseService.pool
.preparedQuery( .preparedQuery(
"UPDATE users SET points=? WHERE id=?") "UPDATE users SET points=? WHERE id=?")
.execute(Tuple.of(points,id)) .execute(Tuple.of(points, id))
.onFailure(e -> { .onFailure(e -> {
System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); System.err.println("Erreur de récupération de la BDD :" + e.getMessage());
context.response() context.response()
@ -41,8 +41,41 @@ public class SetUser {
} }
context.response() context.response()
.putHeader("content-type", "application/json: charset=UTF-8") .putHeader("content-type", "application/json: charset=UTF-8")
.end(new JsonObject().put("success", "Les points de l'utilisateur ont bien été mis à jour").encode()); .end(new JsonObject().put("success", "Les points de l'utilisateur ont bien été mis à jour")
.encode());
return; return;
}); });
} }
public void deleteUser(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");
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){
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;
});
}
} }

View File

@ -1,51 +1,8 @@
import React, { useState,useEffect } from "react"; import React, { useState, useEffect } from "react";
import Sidebar from "./sidebar.jsx"; import Sidebar from "./sidebar.jsx";
import { API_BASE_URL } from "../../config.js"; import { API_BASE_URL } from "../../config.js";
import axios from "axios"; import axios from "axios";
const dashboardStyles = {
mainContent: {
flexGrow: 1,
padding: "20px",
},
summaryContainer: {
display: "flex",
gap: "20px",
marginBottom: "20px",
},
summaryCard: {
background: "#f4f4f4",
padding: "20px",
flex: 1,
borderRadius: "5px",
textAlign: "center",
},
table: {
width: "100%",
borderCollapse: "collapse",
},
tableHeader: {
padding: "10px",
border: "1px solid #ccc",
backgroundColor: "#f4f4f4",
},
tableCell: {
padding: "10px",
border: "1px solid #ccc",
textAlign: "left",
},
viewMoreButton: {
marginTop: "10px",
padding: "10px 15px",
backgroundColor: "#007bff",
color: "#fff",
border: "none",
borderRadius: "5px",
cursor: "pointer",
},
};
function Dashboard() { function Dashboard() {
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
const [logs, setLogs] = useState([ const [logs, setLogs] = useState([
@ -62,64 +19,67 @@ function Dashboard() {
timestamp: new Date().toLocaleString(), timestamp: new Date().toLocaleString(),
}, },
]); ]);
useEffect(() => { useEffect(() => {
axios.get(`${API_BASE_URL}/users`).then((response) => { axios.get(`${API_BASE_URL}/users`).then((response) => {
setUsers(response.data); setUsers(response.data);
}); });
}, []); }, []);
return ( return (
<div style={{ display: "flex", minHeight: "100vh" }}> <div className="flex h-screen">
<Sidebar /> <Sidebar />
<main style={dashboardStyles.mainContent}> <main className="flex-grow p-5">
<h1>Dashboard</h1> <h1 className="text-2xl font-bold mb-6">Dashboard</h1>
<div style={dashboardStyles.summaryContainer}>
<div style={dashboardStyles.summaryCard}> <div className="flex gap-5 mb-6">
<h2>Total Users</h2> <div className="bg-gray-100 p-6 flex-1 rounded-lg text-center">
<p>{users.length}</p> <h2 className="text-xl font-semibold">Total Users</h2>
<p className="text-2xl mt-2">{users.length}</p>
</div> </div>
<div style={dashboardStyles.summaryCard}> <div className="bg-gray-100 p-6 flex-1 rounded-lg text-center">
<h2>Dernier Log</h2> <h2 className="text-xl font-semibold">Dernier Log</h2>
{logs.length > 0 ? ( {logs.length > 0 ? (
<p> <p className="mt-2">
{logs[logs.length - 1].username} -{" "} {logs[logs.length - 1].username} - {logs[logs.length - 1].action}
{logs[logs.length - 1].action}
</p> </p>
) : ( ) : (
<p>Aucun log</p> <p className="mt-2">Aucun log</p>
)} )}
</div> </div>
</div> </div>
<section> <section>
<h2>Aperçu des Utilisateurs</h2> <h2 className="text-xl font-semibold mb-4">Aperçu des Utilisateurs</h2>
<table style={dashboardStyles.table}> <div className="overflow-x-auto">
<table className="min-w-full border border-gray-300">
<thead> <thead>
<tr> <tr className="bg-gray-100">
<th style={dashboardStyles.tableHeader}>Username</th> <th className="p-3 border text-left">Username</th>
<th style={dashboardStyles.tableHeader}>Email</th> <th className="p-3 border text-left">Email</th>
<th style={dashboardStyles.tableHeader}>Niveau d'accès</th> <th className="p-3 border text-left">Niveau d'accès</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{users.slice(0, 5).map((user) => ( {users.slice(0, 5).map((user) => (
<tr key={user.id}> <tr key={user.id} className="hover:bg-gray-50">
<td style={dashboardStyles.tableCell}>{user.name}</td> <td className="p-3 border text-left">{user.name}</td>
<td style={dashboardStyles.tableCell}>{user.email}</td> <td className="p-3 border text-left">{user.email}</td>
<td style={dashboardStyles.tableCell}>{user.role}</td> <td className="p-3 border text-left">{user.role}</td>
</tr> </tr>
))} ))}
{users.length === 0 && ( {users.length === 0 && (
<tr> <tr>
<td colSpan="3" style={dashboardStyles.tableCell}> <td colSpan="3" className="p-3 border text-center">
Aucun utilisateur disponible Aucun utilisateur disponible
</td> </td>
</tr> </tr>
)} )}
</tbody> </tbody>
</table> </table>
{/* Bouton pour accéder à la page complète */} </div>
<button <button
style={dashboardStyles.viewMoreButton} className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
onClick={() => (window.location.href = "/user")} onClick={() => (window.location.href = "/user")}
> >
Voir plus Voir plus

View File

@ -1,51 +1,10 @@
import React, { useState,useEffect} from "react"; import React, { useState, useEffect } from "react";
import Sidebar from "./sidebar.jsx"; import Sidebar from "./sidebar.jsx";
import { API_BASE_URL } from "../../config.js"; import { API_BASE_URL } from "../../config.js";
import axios from "axios"; import axios from "axios";
const styles = { const thTd = "p-2 border border-gray-300 text-left";
mainContent: { const th = `${thTd} bg-gray-100`;
flexGrow: 1,
padding: "20px",
},
header: {
marginBottom: "20px",
},
userManagement: {
marginTop: "20px",
},
userForm: {
display: "grid",
gridTemplateColumns: "1fr 1fr auto",
gap: "10px",
marginBottom: "20px",
},
input: {
padding: "10px",
border: "1px solid #ccc",
borderRadius: "5px",
},
button: {
padding: "10px",
backgroundColor: "#28a745",
color: "white",
border: "none",
borderRadius: "5px",
cursor: "pointer",
},
userTable: {
width: "100%",
borderCollapse: "collapse",
},
thTd: {
padding: "10px",
border: "1px solid #ccc",
textAlign: "left",
},
th: {
backgroundColor: "#f4f4f4",
},
};
function User() { function User() {
const [users, setUsers] = useState([]); const [users, setUsers] = useState([]);
@ -64,7 +23,7 @@ function User() {
points: 0, points: 0,
}; };
setUsers([...users, newUser]); setUsers([...users, newUser]);
logAction(name, "User added"); logAction(name, "Utilisateur ajouté");
setname(""); setname("");
setEmail(""); setEmail("");
}; };
@ -76,7 +35,22 @@ function User() {
const handleDeleteUser = (userId) => { const handleDeleteUser = (userId) => {
const user = users.find((u) => u.id === userId); const user = users.find((u) => u.id === userId);
if (user) { if (user) {
logAction(user.name, "User deleted"); axios
.post(`${API_BASE_URL}/deleteUser`, {
id: userId,
})
.then((response) => {
alert("L'utilisateur à bien été supprimé !");
console.log("L'utilisateur à été supprimé :", response.data);
window.location.reload();
})
.catch((error) => {
console.error(
"Erreur lors de la suppression de l'utilisateur :",
error
);
});
logAction(user.name, "Utilisateur supprimé");
} }
setUsers(users.filter((u) => u.id !== userId)); setUsers(users.filter((u) => u.id !== userId));
}; };
@ -84,13 +58,33 @@ function User() {
const handleChangeAccessLevel = (userId, newLevel) => { const handleChangeAccessLevel = (userId, newLevel) => {
setUsers( setUsers(
users.map((user) => { users.map((user) => {
if (user.id === userId) { if (user.id === userId && newLevel!=user.role) {
const oldLevel = user.role; const oldLevel = user.role;
user.role = newLevel; user.role = newLevel;
/*ToDO*/ /*ToDO*/
if (user.role === "user") {
user.points = 0;
} else if (user.role === "complexe") {
user.points = 60;
} else if (user.role === "admin") {
user.points = 100;
}
axios
.post(`${API_BASE_URL}/setUserPoints`, {
id: user.id,
points: user.points,
})
.then((response) => {
alert("Le changement de niveau a bien été enregistré !");
console.log("Changement de niveau réussit :", response.data);
})
.catch((error) => {
alert("Il y a eu une erreur dans le changement de niveau !");
console.error("Erreur lors du changement de niveau :", error);
});
logAction( logAction(
user.name, user.name,
`Access level changed from ${oldLevel} to ${newLevel}` `Niveau d'accés changé de ${oldLevel} à ${newLevel}`
); );
} }
return user; return user;
@ -103,19 +97,18 @@ function User() {
setUsers( setUsers(
users.map((user) => { users.map((user) => {
if (user.id === userId) { if (user.id === userId) {
user.points += pointsToAdd; user.points = pointsToAdd;
axios axios
.post(`${API_BASE_URL}/setUserPoints`, { .post(`${API_BASE_URL}/setUserPoints`, {
id:user.id, id: user.id,
points:user.points, points: user.points,
}) })
.then((response) => { .then((response) => {
setMessRequete("Les points ont bien été enregistré !"); alert("Les points ont bien été enregistré !");
setEnregistre(true);
console.log("Ajout des points réussit :", response.data); console.log("Ajout des points réussit :", response.data);
}) })
.catch((error) => { .catch((error) => {
setMessRequete("Il y a eu une erreur dans l'ajout des points!"); alert("Il y a eu une erreur dans l'ajout des points!");
console.error("Erreur lors de l'ajout des points :", error); console.error("Erreur lors de l'ajout des points :", error);
}); });
logAction(user.name, `Points ajustés par ${pointsToAdd}`); logAction(user.name, `Points ajustés par ${pointsToAdd}`);
@ -134,16 +127,21 @@ function User() {
}; };
return ( return (
<div style={{ display: "flex", minHeight: "100vh" }}> <div className="flex min-h-screen">
<Sidebar /> <Sidebar />
<main style={styles.mainContent}> <main className="flex-grow p-5">
<section style={styles.userManagement}> <section className="mt-5">
<h1>User Management</h1> <h1 className="text-2xl font-bold text-gray-900 mb-5">
User Management
</h1>
<p>Manage users from this panel.</p> <p>Manage users from this panel.</p>
{/* Formulaire d'ajout d'utilisateur */} {/* Formulaire d'ajout d'utilisateur */}
<form style={styles.userForm} onSubmit={handleAddUser}> <form
className="gap-3 mb-5 grid grid-cols-[1fr_1fr_auto]"
onSubmit={handleAddUser}
>
<input <input
style={styles.input} className="p-3 border rounded-md"
type="text" type="text"
id="name" id="name"
placeholder="name" placeholder="name"
@ -152,7 +150,7 @@ function User() {
required required
/> />
<input <input
style={styles.input} className="p-3 border rounded-md"
type="email" type="email"
id="email" id="email"
placeholder="Email" placeholder="Email"
@ -160,49 +158,50 @@ function User() {
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
required required
/> />
<button style={styles.button} type="submit"> <button
Add User className="p-3 bg-green-600 text-white border-none rounded-md "
type="submit"
>
Ajouter utilisateur
</button> </button>
</form> </form>
{/* Tableau des utilisateurs */} {/* Tableau des utilisateurs */}
<table style={styles.userTable}> <table className="w-full">
<thead> <thead>
<tr> <tr>
<th style={{ ...styles.thTd, ...styles.th }}>Nom</th> <th className={`${th}`}>Nom</th>
<th style={{ ...styles.thTd, ...styles.th }}>Email</th> <th className={`${th}`}>Email</th>
<th style={{ ...styles.thTd, ...styles.th }}>Niveau d'accès</th> <th className={`${th}`}>Niveau d'accès</th>
<th style={{ ...styles.thTd, ...styles.th }}>Points</th> <th className={`${th}`}>Points</th>
<th style={{ ...styles.thTd, ...styles.th }}>Actions</th> <th className={`${th}`}>Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{users.map((user) => ( {users.map((user) => (
<tr key={user.id}> <tr key={user.id}>
<td style={styles.thTd}>{user.name}</td> <td className={`${thTd}`}>{user.name}</td>
<td style={styles.thTd}>{user.email}</td> <td className={`${thTd}`}>{user.email}</td>
<td style={styles.thTd}> <td className={`${thTd}`}>
{/* Menu déroulant pour changer le niveau d'accès */} {/* Menu déroulant pour changer le niveau d'accès */}
<select <select
value={user.role} value={user.role}
onChange={(e) => onChange={(e) =>
handleChangeAccessLevel(user.id, e.target.value) handleChangeAccessLevel(user.id, e.target.value)
} }
style={{ padding: "5px", borderRadius: "5px" }} className="p-2 rounded-md"
> >
<option value="admin">Admin</option> <option value="admin">Admin</option>
<option value="user">User</option> <option value="user">User</option>
<option value="complexe">Complexe</option> <option value="complexe">Complexe</option>
</select> </select>
</td> </td>
<td style={styles.thTd}> <td className={`${thTd}`}>
<span>{user.points}</span>
{/* Input et bouton pour ajuster les points */} {/* Input et bouton pour ajuster les points */}
<input <input
className="border ml-4 w-16"
type="number" type="number"
min="0" min="0"
style={{ width: "60px", marginLeft: "10px" }} value={pointsInput[user.id] || user.points}
placeholder="Adjust"
value={pointsInput[user.id] || ""}
onChange={(e) => onChange={(e) =>
setPointsInput({ setPointsInput({
...pointsInput, ...pointsInput,
@ -211,27 +210,19 @@ function User() {
} }
/> />
<button <button
style={{ className="p-2 bg-green-600 text-white border-none rounded-md"
...styles.button,
padding: "5px 10px",
marginLeft: "5px",
}}
onClick={() => handleAdjustPoints(user.id)} onClick={() => handleAdjustPoints(user.id)}
> >
Adjust Changer
</button> </button>
</td> </td>
<td style={styles.thTd}> <td className={`${thTd}`}>
{/* Bouton de suppression */} {/* Bouton de suppression */}
<button <button
style={{ className="p-2 bg-red-600 text-white border-none rounded-md"
...styles.button,
backgroundColor: "#dc3545",
padding: "5px 10px",
}}
onClick={() => handleDeleteUser(user.id)} onClick={() => handleDeleteUser(user.id)}
> >
Delete Supprimer
</button> </button>
</td> </td>
</tr> </tr>
@ -240,22 +231,24 @@ function User() {
</table> </table>
</section> </section>
{/* Tableau des logs */} {/* Tableau des logs */}
<section className="user-logs" style={{ marginTop: "40px" }}> <section className="user-logs mt-10">
<h2>Historique des connexions et journal des logs </h2> <h2 className="text-2xl font-bold text-gray-900 mb-5">
<table style={styles.userTable}> Historique des connexions et journal des logs{" "}
</h2>
<table className="w-full">
<thead> <thead>
<tr> <tr>
<th style={{ ...styles.thTd, ...styles.th }}>Nom</th> <th className={`${th}`}>Nom</th>
<th style={{ ...styles.thTd, ...styles.th }}>Action</th> <th className={`${th}`}>Action</th>
<th style={{ ...styles.thTd, ...styles.th }}>Timestamp</th> <th className={`${th}`}>Timestamp</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{logs.map((log) => ( {logs.map((log) => (
<tr key={log.id}> <tr key={log.id}>
<td style={styles.thTd}>{log.name}</td> <td className={`${thTd}`}>{log.name}</td>
<td style={styles.thTd}>{log.action}</td> <td className={`${thTd}`}>{log.action}</td>
<td style={styles.thTd}>{log.timestamp}</td> <td className={`${thTd}`}>{log.timestamp}</td>
</tr> </tr>
))} ))}
</tbody> </tbody>