diff --git a/.vscode/settings.json b/.vscode/settings.json index 5ed7c7a..bbca028 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { "liveServer.settings.port": 8081, - "java.configuration.updateBuildConfiguration": "interactive" + "java.configuration.updateBuildConfiguration": "automatic" } \ No newline at end of file diff --git a/Back-end/pom.xml b/Back-end/pom.xml index 5d0ce82..b221038 100644 --- a/Back-end/pom.xml +++ b/Back-end/pom.xml @@ -88,10 +88,10 @@ 0.9.0 - io.vertx - vertx-auth-jwt - 4.5.13 - + io.vertx + vertx-auth-jwt + 4.5.13 + 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 2052b71..cb0d0cd 100644 --- a/Back-end/src/main/java/com/example/starter/AuthHandler.java +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -4,7 +4,6 @@ import io.vertx.core.json.JsonObject; import io.vertx.ext.web.RoutingContext; import at.favre.lib.crypto.bcrypt.BCrypt; import io.vertx.ext.auth.jwt.JWTAuth; -import com.example.starter.auth.JwtAuthProvider; import io.vertx.sqlclient.Tuple; public class AuthHandler { @@ -78,7 +77,7 @@ public class AuthHandler { } databaseService.pool - .preparedQuery("SELECT password FROM users WHERE email = ?") + .preparedQuery("SELECT password,points FROM users WHERE email = ?") .execute(Tuple.of(email)) .onSuccess(result -> { if (result.rowCount() == 0) { @@ -89,10 +88,20 @@ public class AuthHandler { } String storedHashedPassword = result.iterator().next().getString("password"); + Integer nbPointsUser = result.iterator().next().getInteger("points"); + BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); if (verification.verified) { - JsonObject claims = new JsonObject().put("sub", email).put("role", "user"); + JsonObject claims = new JsonObject().put("sub", email); + if(nbPointsUser<=30){ + claims.put("role", "user"); + }else if(nbPointsUser<=60){ + claims.put("role", "complexe"); + }else if(nbPointsUser>=100){ + claims.put("role", "admin"); + } + String token = jwtAuth.generateToken(claims); context.response() .setStatusCode(200) diff --git a/Back-end/src/main/java/com/example/starter/DatabaseService.java b/Back-end/src/main/java/com/example/starter/DatabaseService.java index f4e05d5..8742f41 100644 --- a/Back-end/src/main/java/com/example/starter/DatabaseService.java +++ b/Back-end/src/main/java/com/example/starter/DatabaseService.java @@ -3,8 +3,7 @@ package com.example.starter; import io.vertx.core.Vertx; import io.vertx.jdbcclient.JDBCConnectOptions; import io.vertx.jdbcclient.JDBCPool; -import io.vertx.ext.auth.jwt.JWTAuth; -import io.vertx.ext.auth.jwt.JWTAuthOptions; + import io.vertx.sqlclient.PoolOptions; public class DatabaseService { @@ -13,7 +12,7 @@ public class DatabaseService { public DatabaseService(Vertx vertx) { pool = JDBCPool.pool(vertx, new JDBCConnectOptions() - .setJdbcUrl("jdbc:postgresql://localhost:5432/users?useUnicode=true&characterEncoding=UTF-8") //Url de la bdd + .setJdbcUrl("jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=UTF-8") //Url de la bdd .setUser("postgres") // Nom d'utilisateur PostgreSQL .setPassword("admin"), // Mot de passe PostgreSQL new PoolOptions() diff --git a/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java index 01d26d9..6c3cd38 100644 --- a/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java +++ b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java @@ -1,10 +1,10 @@ -package com.example.starter.auth; +package com.example.starter; import io.vertx.core.Vertx; import io.vertx.ext.auth.jwt.JWTAuth; import io.vertx.ext.auth.jwt.JWTAuthOptions; import io.vertx.ext.auth.KeyStoreOptions; -import com.example.starter.auth.JwtAuthProvider; +import com.example.starter.JwtAuthProvider; public class JwtAuthProvider { 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 00a2cd1..052e187 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -7,10 +7,8 @@ import io.vertx.ext.web.Router; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.CorsHandler; import io.vertx.ext.auth.jwt.JWTAuth; -import com.example.starter.auth.JwtAuthProvider; import io.vertx.ext.web.handler.JWTAuthHandler; - public class MainVerticle extends AbstractVerticle { private DatabaseService databaseService; private Router router; @@ -18,32 +16,32 @@ public class MainVerticle extends AbstractVerticle { @Override public void start(Promise startPromise) throws Exception { databaseService = new DatabaseService(vertx); - + // Initialisation du fournisseur JWT JWTAuth jwtAuth = JwtAuthProvider.createJwtAuth(vertx); - // Initialisation du routeur router = Router.router(vertx); router.route().handler(BodyHandler.create()); router.route().handler(CorsHandler.create() - .addOrigin("*") - .allowedMethod(HttpMethod.GET) - .allowedMethod(HttpMethod.POST) - .allowedHeader("Content-Type") - .allowedHeader("Authorization")); - + .addOrigin("*") + .allowedMethod(HttpMethod.GET) + .allowedMethod(HttpMethod.POST) + .allowedHeader("Content-Type") + .allowedHeader("Authorization")); + // Protéger toutes les routes commençant par "/api/" router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth)); - // Initialisation des handlers de requêtes QueryObjects queryObjects = new QueryObjects(databaseService); QueryWeatherData queryWeather = new QueryWeatherData(databaseService); SetObjects setObjects = new SetObjects(databaseService); - SetWeatherData setWeatherData = new SetWeatherData(databaseService); + SetWeatherData setWeatherData = new SetWeatherData(databaseService); AuthHandler authHandler = new AuthHandler(databaseService, jwtAuth); - + QueryUsers queryUsers = new QueryUsers(databaseService); + SetUser setUser = new SetUser(databaseService); + // Déclaration des routes router.get("/objets").handler(queryObjects::getObjects); router.get("/objet").handler(queryObjects::getParticularObject); @@ -53,22 +51,24 @@ public class MainVerticle extends AbstractVerticle { router.post("/addObject").handler(setObjects::newObject); router.get("/getRange").handler(queryWeather::getRangeData); router.post("/modifRangeData").handler(setWeatherData::setRangeData); - + router.post("/deleteObject").handler(setObjects::deleteObject); + router.get("/users").handler(queryUsers::getUsers); + router.post("/setUserPoints").handler(setUser::setUserPoints); // Routes d'authentification router.post("/signup").handler(authHandler::handleSignup); router.post("/login").handler(authHandler::handleLogin); - + // Création du serveur HTTP vertx.createHttpServer() - .requestHandler(router) - .listen(8888) - .onSuccess(server -> { - System.out.println("HTTP server started on port " + server.actualPort()); - startPromise.complete(); - }) - .onFailure(throwable -> { - throwable.printStackTrace(); - startPromise.fail(throwable); - }); + .requestHandler(router) + .listen(8888) + .onSuccess(server -> { + System.out.println("HTTP server started on port " + server.actualPort()); + startPromise.complete(); + }) + .onFailure(throwable -> { + throwable.printStackTrace(); + startPromise.fail(throwable); + }); } } diff --git a/Back-end/src/main/java/com/example/starter/QueryObjects.java b/Back-end/src/main/java/com/example/starter/QueryObjects.java index fe186fc..8d90bf0 100644 --- a/Back-end/src/main/java/com/example/starter/QueryObjects.java +++ b/Back-end/src/main/java/com/example/starter/QueryObjects.java @@ -78,7 +78,8 @@ public class QueryObjects { .put("last_update", row.getLocalDateTime("last_update").format(formatter)) .put("status", row.getString("status")) .put("batterie",row.getInteger("batterie")) - .put("type_batterie",row.getString("type_batterie")); + .put("type_batterie",row.getString("type_batterie")) + .put("proprio",row.getString("proprio")); objects.add(object); } return objects; diff --git a/Back-end/src/main/java/com/example/starter/QueryUsers.java b/Back-end/src/main/java/com/example/starter/QueryUsers.java new file mode 100644 index 0000000..a3cca49 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/QueryUsers.java @@ -0,0 +1,51 @@ +package com.example.starter; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.sqlclient.Row; + +public class QueryUsers { + private DatabaseService databaseService; + + public QueryUsers(DatabaseService dtbS) { + this.databaseService = dtbS; + } + + public void getUsers(RoutingContext context) { + databaseService.pool + .query("SELECT * FROM users;") + .execute() + .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 -> { + JsonArray users = new JsonArray(); + for (Row row : rows) { + 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<=30){ + user.put("role", "user"); + }else if(points<=60){ + user.put("role", "complexe"); + }else if(points>=100){ + user.put("role", "admin"); + } + users.add(user); + } + context.response() + .putHeader("content-type", "application/json; charset=UTF-8") + .end(users.encode()); + + }); + } +} diff --git a/Back-end/src/main/java/com/example/starter/SetObjects.java b/Back-end/src/main/java/com/example/starter/SetObjects.java index dd35436..e3a6de6 100644 --- a/Back-end/src/main/java/com/example/starter/SetObjects.java +++ b/Back-end/src/main/java/com/example/starter/SetObjects.java @@ -48,7 +48,38 @@ public class SetObjects { return; }); } + public void deleteObject(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; + } + String id = body.getString("id"); + databaseService.pool + .preparedQuery("DELETE FROM weather_objects WHERE id=?") + .execute(Tuple.of(Integer.parseInt(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", "Objet non trouvé").encode()); + return; + } + context.response() + .putHeader("content-type","application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'objet à bien été supprimé").encode()); + return; + }); + } public void newObject(RoutingContext context){ JsonObject body = context.body().asJsonObject(); if(body== null){ @@ -62,9 +93,11 @@ public class SetObjects { String type = body.getString("type"); String location = body.getString("location"); String status = body.getString("status"); + String batterieType = body.getString("batterieType"); + String proprio = body.getString("proprio"); databaseService.pool - .preparedQuery("INSERT INTO weather_objects (name,description,type,location,status) VALUES (?,?,?,?,?)") - .execute(Tuple.of(name,description,type,location,status)) + .preparedQuery("INSERT INTO weather_objects (name,description,type,location,status,type_batterie,proprio) VALUES (?,?,?,?,?,?,?)") + .execute(Tuple.of(name,description,type,location,status,batterieType,proprio)) .onFailure(e->{ System.err.println("Erreur de récupération de la BDD :"+e.getMessage()); context.response() diff --git a/Back-end/src/main/java/com/example/starter/SetUser.java b/Back-end/src/main/java/com/example/starter/SetUser.java new file mode 100644 index 0000000..9d104fd --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/SetUser.java @@ -0,0 +1,48 @@ +package com.example.starter; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.sqlclient.Tuple; + +public class SetUser { + private DatabaseService databaseService; + + public SetUser(DatabaseService ddbs) { + this.databaseService = ddbs; + } + + public void setUserPoints(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"); + Integer points = body.getInteger("points"); + + databaseService.pool + .preparedQuery( + "UPDATE users SET points=? WHERE id=?") + .execute(Tuple.of(points,id)) + .onFailure(e -> { + System.err.println("Erreur de récupération de la BDD :" + e.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("Erreur", "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 points de l'utilisateur ont bien été mis à jour").encode()); + return; + }); + } +} diff --git a/Front-end/index.html b/Front-end/index.html index e0bb005..63968ac 100644 --- a/Front-end/index.html +++ b/Front-end/index.html @@ -4,7 +4,7 @@ - + Projet Dev Web diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index 3059717..5b1d6c8 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -18,8 +18,6 @@ import AdminObjet from "./pages/Admin/AdminObjet.jsx"; function App() { return ( - {" "} - {/* Enveloppe l'application avec AuthProvider */}
diff --git a/Front-end/src/components/FormNewObject.jsx b/Front-end/src/components/FormNewObject.jsx new file mode 100644 index 0000000..662f5e4 --- /dev/null +++ b/Front-end/src/components/FormNewObject.jsx @@ -0,0 +1,257 @@ +import React, { useState } from "react"; +import { BadgePlus } from "lucide-react"; +import axios from "axios"; +import { API_BASE_URL } from "../config"; + +function FormNewObject({ isAdmin }) { + const [description, setDescription] = useState(""); + const [type, setType] = useState(""); + const [location, setLocalisation] = useState(""); + const [proprio,setProprio] = useState(""); + const [batterieType,setBatterieType] = useState(""); + /*TODO*/ + /*Definir proprio avec le nom de l'user qui ajoute*/ + const [status, setStatus] = useState("active"); + const [nom, setNom] = useState(""); + const [Response, setResponse] = useState(null); + const [isActive, setActive] = useState(true); + const [verif, setVerif] = useState(false); + const [enregistre, setEnregistre] = useState(false); + const [messRequete, setMessRequete] = useState(""); + function handleSubmit(event) { + event.preventDefault(); + + if (verif) { + console.log("Envoi requete"); + axios + .post(`${API_BASE_URL}/addObject`, { + nom, + description, + type, + location, + status, + batterieType, + proprio + }) + .then((response) => { + setMessRequete("Votre objet à bien été enregistré !"); + setEnregistre(true); + console.log("Ajout de l'objet réussit :", response.data); + }) + .catch((error) => { + setMessRequete("Il y a eu une erreur dans l'ajout de votre objet !"); + console.error("Erreur lors de l'ajout de l'objet :", error); + }); + setVerif(false); + resetForm(); + } else { + setVerif(true); + } + } + function resetForm() { + setNom(""); + setStatus("active"); + setDescription(""); + setType(""); + setLocalisation(""); + setBatterieType(""); + if(isAdmin)setProprio(""); + setActive(true); + } + function handleCancel() { + if (verif) { + setVerif(false); + } else { + resetForm(); + } + } + function handleStatusChange() { + setActive((prevIsActive) => { + const newIsActive = !prevIsActive; + setStatus(newIsActive ? "active" : "inactive"); + return newIsActive; + }); + } + + return ( +
+
+ {isAdmin ? ( +

+ Ajouter un nouvel objet +

+ ) : ( + <> +
+ +
+

+ {!verif + ? "Entrez les données de votre nouvel objet" + : "Êtes-vous sûr de ces données ?"} +

+ + )} +
+
+ + setNom(e.target.value)} + required + disabled={verif} + /> +
+
+ + setDescription(e.target.value)} + required + disabled={verif} + /> +
+ +
+ + setType(e.target.value)} + required + disabled={verif} + /> +
+ +
+ + setLocalisation(e.target.value)} + required + disabled={verif} + /> +
+
+ + setBatterieType(e.target.value)} + required + disabled={verif} + /> +
+
+ + setProprio(e.target.value)} + required + disabled={verif||!isAdmin} + /> +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ + +
+

+ {messRequete} +

+
+ ); +} + +export default FormNewObject; diff --git a/Front-end/src/components/Header.jsx b/Front-end/src/components/Header.jsx index 88a8764..3c4c93b 100644 --- a/Front-end/src/components/Header.jsx +++ b/Front-end/src/components/Header.jsx @@ -1,125 +1,202 @@ import React, { useState } from "react"; -import { LogIn, UserPlus, LogOut, Settings } from "lucide-react"; +import { X, Menu, LogIn, UserPlus, LogOut, Settings } from "lucide-react"; import { Link } from "react-router-dom"; import { useAuth } from "../AuthContext"; function Header() { const { token, 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); }; + // Pour l'instant, le menu "Admin" est toujours affiché. + // TODO: Par la suite, ajoutez une vérification du rôle utilisateur (ex: token && user.role === "admin") + // afin d'afficher ce menu uniquement aux administrateurs. return ( -
-
-
-

VigiMétéo

- + {!token ? ( +
+ + + Connexion + + + + Inscription +
-
+ ) : ( +
+ + + + + {/* Menu déroulant Admin visible sur sm+ */} +
+ + {showAdminDropdown && ( +
+ setShowAdminDropdown(false)} + > + Dashboard + + setShowAdminDropdown(false)} + > + Gestion des Utilisateurs + + setShowAdminDropdown(false)} + > + Gestion des Objets Connectés + +
+ )} +
+
+ )}
); diff --git a/Front-end/src/components/ModifObject.jsx b/Front-end/src/components/ModifObject.jsx index b3658d1..9637c1d 100644 --- a/Front-end/src/components/ModifObject.jsx +++ b/Front-end/src/components/ModifObject.jsx @@ -11,7 +11,7 @@ function ModifObject({ object, defafficherModif }) { const [isActive, setActive] = useState(object.status === "active"); function handleSubmit(event) { - event.preventDefault(); // Empêche le rechargement de la page + event.preventDefault(); axios .post(`${API_BASE_URL}/modifObjet`, { id: object.id, diff --git a/Front-end/src/img/cloud-alert.svg b/Front-end/src/img/cloud-alert.svg deleted file mode 100644 index a7ea487..0000000 --- a/Front-end/src/img/cloud-alert.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Front-end/src/img/cloud-sun-rain.svg b/Front-end/src/img/cloud-sun-rain.svg new file mode 100644 index 0000000..9892ac6 --- /dev/null +++ b/Front-end/src/img/cloud-sun-rain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Front-end/src/pages/Admin/AdminObjet.jsx b/Front-end/src/pages/Admin/AdminObjet.jsx index 90a5ee1..c6bfd05 100644 --- a/Front-end/src/pages/Admin/AdminObjet.jsx +++ b/Front-end/src/pages/Admin/AdminObjet.jsx @@ -1,10 +1,20 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import Sidebar from "./sidebar.jsx"; +import axios from "axios"; +import { API_BASE_URL } from "../../config"; +import AddObject from "../Gestion/AddObject.jsx"; +import FormNewObject from "../../components/FormNewObject.jsx"; function AdminObjet() { - // Gestion des catégories const [categories, setCategories] = useState(["Catégorie 1", "Catégorie 2"]); const [newCategory, setNewCategory] = useState(""); + const [objects, setObjects] = useState([]); + + useEffect(() => { + axios.get(`${API_BASE_URL}/objets`).then((response) => { + setObjects(response.data); + }); + }, []); const handleAddCategory = () => { const trimmed = newCategory.trim(); @@ -13,23 +23,16 @@ function AdminObjet() { setNewCategory(""); } }; - const handleDeleteCategory = (categoryToDelete) => { setCategories(categories.filter((cat) => cat !== categoryToDelete)); }; - // Gestion des objets et outils/services - // On ajoute maintenant le champ "propriétaire" - const [objects, setObjects] = useState([]); - - // Champs du formulaire d'ajout d'objet (inspiré de AddObject.jsx) - const [nom, setNom] = useState(""); + const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [type, setType] = useState(""); - const [localisation, setLocalisation] = useState(""); + const [location, setLocation] = useState(""); const [proprietaire, setProprietaire] = useState(""); const [status, setStatus] = useState("active"); - const [isActive, setIsActive] = useState(true); const [verif, setVerif] = useState(false); const [enregistre, setEnregistre] = useState(false); const [messRequete, setMessRequete] = useState(""); @@ -39,10 +42,10 @@ function AdminObjet() { if (verif) { const newObj = { id: Date.now(), - nom: nom.trim(), + name: name.trim(), description: description.trim(), type: type.trim(), - localisation: localisation.trim(), + location: location.trim(), proprietaire: proprietaire.trim(), status: status, }; @@ -51,42 +54,29 @@ function AdminObjet() { setEnregistre(true); setVerif(false); resetForm(); + window.location.reload(); } else { setVerif(true); } }; - - const resetForm = () => { - setNom(""); - setDescription(""); - setType(""); - setLocalisation(""); - setProprietaire(""); - setStatus("active"); - setIsActive(true); - }; - - const handleCancel = () => { - if (verif) { - setVerif(false); - } else { - resetForm(); - } - }; - - const handleStatusChange = () => { - setIsActive((prev) => { - const newIsActive = !prev; - setStatus(newIsActive ? "active" : "inactive"); - return newIsActive; - }); - }; - const handleDeleteObject = (id) => { - setObjects(objects.filter((obj) => obj.id !== id)); + axios + .post(`${API_BASE_URL}/deleteObject`, { + id, + }) + .then((response) => { + setMessRequete("Votre objet à bien été supprimé !"); + console.log("Votre objet à été supprimé :", response.data); + window.location.reload(); + }) + .catch((error) => { + setMessRequete( + "Il y a eu une erreur dans la suppression de votre objet !" + ); + console.error("Erreur lors de la suppression de l'objet :", error); + }); }; - // Tri des objets en fonction d'une catégorie sélectionnée const [sortCriteria, setSortCriteria] = useState(""); const sortedObjects = [...objects].sort((a, b) => { @@ -96,7 +86,6 @@ function AdminObjet() { return fieldA.localeCompare(fieldB); }); - // Règles globales const [energyPriority, setPriority] = useState(""); const [alertSettings, setAlertSettings] = useState(""); @@ -154,138 +143,11 @@ function AdminObjet() { ))} + {/*Formulaire d'ajout d'objet*/} + - {/* Formulaire d'ajout d'objet */} -
-
-
- + -
-

- {!verif - ? "Entrez les données de votre nouvel objet" - : "Êtes-vous sûr de ces données ?"} -

-
-
-
- - setNom(e.target.value)} - required - disabled={verif} - className="w-full p-2 border border-gray-300 rounded-lg text-gray-600" - /> -
-
- - setDescription(e.target.value)} - required - disabled={verif} - className="w-full p-2 border border-gray-300 rounded-lg text-gray-600" - /> -
-
- - setType(e.target.value)} - required - disabled={verif} - className="w-full p-2 border border-gray-300 rounded-lg text-gray-600" - /> -
-
- - setLocalisation(e.target.value)} - required - disabled={verif} - className="w-full p-2 border border-gray-300 rounded-lg text-gray-600" - /> -
-
- - setProprietaire(e.target.value)} - required - disabled={verif} - className="w-full p-2 border border-gray-300 rounded-lg text-gray-600" - /> -
-
- -
- -
- - -
- -
-
-
- - -
-

- {messRequete} -

-
-
- {/* TRI DES OBJETS */} -
+ {/* Tri des objets */} +

Liste des Objets et Outils/Services @@ -297,7 +159,7 @@ function AdminObjet() { > - + @@ -332,7 +194,7 @@ function AdminObjet() { {sortedObjects.map((obj) => ( - {obj.nom} + {obj.name} {obj.description} @@ -341,10 +203,10 @@ function AdminObjet() { {obj.type} - {obj.localisation} + {obj.location} - {obj.proprietaire} + {obj.proprio} {obj.status} diff --git a/Front-end/src/pages/Admin/Dashboard.jsx b/Front-end/src/pages/Admin/Dashboard.jsx index 2015ff5..0d823f4 100644 --- a/Front-end/src/pages/Admin/Dashboard.jsx +++ b/Front-end/src/pages/Admin/Dashboard.jsx @@ -1,6 +1,8 @@ import React, { useState } from "react"; import Sidebar from "./sidebar.jsx"; import { RadioTower, ArrowRight, BadgePlus, Settings } from "lucide-react"; +import { API_BASE_URL } from "../../config.js"; +import axios from "axios"; // Widgets initiaux pour le dashboard const initialWidgets = [ @@ -109,7 +111,13 @@ function Dashboard() { setWidgets([...widgets, newWidget]); setShowAddWidgetModal(false); }; - + /* + useEffect(() => { + axios.get(`${API_BASE_URL}/users`).then((response) => { + setUsers(response.data); + }); + }, []); + */ return (
@@ -148,10 +156,10 @@ function Dashboard() { {widget.type === "summary" && (

- Dashboard Summary + Résumé du tableau de bord

-

Total Users

+

Total Utilisateur

{users.length}

@@ -167,6 +175,7 @@ function Dashboard() {
)} + {widget.type === "users" && (

@@ -222,6 +231,7 @@ function Dashboard() {

)} + {widget.type === "objects" && (

@@ -339,4 +349,4 @@ function Dashboard() { ); } -export default Dashboard; +export default Dashboard; \ No newline at end of file diff --git a/Front-end/src/pages/Admin/User.jsx b/Front-end/src/pages/Admin/User.jsx index db131ff..235e35f 100644 --- a/Front-end/src/pages/Admin/User.jsx +++ b/Front-end/src/pages/Admin/User.jsx @@ -1,10 +1,16 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; 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 [username, setUsername] = useState(""); + const [name, setName] = useState(""); const [email, setEmail] = useState(""); const [pointsInput, setPointsInput] = useState({}); @@ -12,70 +18,113 @@ function User() { const handleAddUser = (e) => { e.preventDefault(); const newUser = { - id: Date.now(), - username, + id: Date.now(), // ID généré temporairement ; en production, il sera géré par la BDD. + name, email, - accessLevel: "User", // Niveau d'accès par défaut + accessLevel: "User", points: 0, }; setUsers([...users, newUser]); - logAction(username, "User added"); - setUsername(""); + logAction(name, "User added"); + 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) + ); + }, []); + // Suppression d'un utilisateur const handleDeleteUser = (userId) => { const user = users.find((u) => u.id === userId); if (user) { - logAction(user.username, "User deleted"); + logAction(user.name, "User deleted"); } setUsers(users.filter((u) => u.id !== userId)); + // TODO : Envoyer la suppression au backend. }; - // Changement du niveau d'accès via le menu déroulant + // Changement du niveau d'accès const handleChangeAccessLevel = (userId, newLevel) => { setUsers( users.map((user) => { - if (user.id === userId) { + if (user.id === userId && newLevel !== user.accessLevel) { const oldLevel = user.accessLevel; user.accessLevel = newLevel; - logAction( - user.username, - `Access level changed from ${oldLevel} to ${newLevel}` - ); + // Exemple de remise à zéro ou affectation de points en fonction du rôle : + if (newLevel === "user") { + user.points = 0; + } else if (newLevel === "complexe") { + user.points = 60; + } else if (newLevel === "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("Niveau changé:", response.data); + }) + .catch((error) => { + alert("Erreur lors du changement de niveau !"); + console.error(error); + }); + logAction(user.name, `Access level changed from ${oldLevel} to ${newLevel}`); } return user; }) ); }; - // Ajustement des points d'un utilisateur + // 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; - logAction(user.username, `Points adjusted by ${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); + }) + .catch((error) => { + alert("Erreur lors de l'ajustement des points !"); + console.error(error); + }); + logAction(user.name, `Points adjusted by ${pointsToAdd}, new total: ${user.points}`); } return user; }) ); - // Réinitialiser l'input pour cet utilisateur setPointsInput({ ...pointsInput, [userId]: "" }); }; // Fonction de journalisation des actions - const logAction = (username, action) => { + const logAction = (name, action) => { const timestamp = new Date().toLocaleString(); - setLogs([...logs, { id: Date.now(), username, action, timestamp }]); + setLogs([...logs, { id: Date.now(), name, action, timestamp }]); }; - // Fonction pour générer et télécharger le fichier txt des logs + // 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.username} - ${log.action}`) + .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"); @@ -95,17 +144,17 @@ function User() {

Gérez les utilisateurs à partir de ce panneau.

{/* Formulaire d'ajout d'utilisateur */}
setUsername(e.target.value)} + id="name" + placeholder="Name" + value={name} + onChange={(e) => setName(e.target.value)} required - className="p-2.5 border border-gray-300 rounded-md" + className="p-2 border border-gray-300 rounded-md" /> setEmail(e.target.value)} required - className="p-2.5 border border-gray-300 rounded-md" + className="p-2 border border-gray-300 rounded-md" />
{/* Tableau des utilisateurs */} - +
- - - - - + + + + + {users.map((user) => ( - - - + + - - @@ -203,40 +236,27 @@ function User() { {/* Tableau des logs */}
-

- Login History and Action Logs +

+ Historique des connexions et journal des logs

-
- Username - - Email - - Access Level - - Points - - Actions - NomEmailNiveau d'accèsPointsActions
- {user.username} - - {user.email} - + {user.name}{user.email} - {user.points} + setPointsInput({ ...pointsInput, [user.id]: e.target.value, }) } - className="w-[60px] ml-2.5 p-1 border border-gray-300 rounded-md" + className="w-16 ml-2 p-1 border border-gray-300 rounded-md" /> +
+
- - - + + + {logs.map((log) => ( - - - + + + ))}
- Username - - Action - - Timestamp - NomActionTimestamp
- {log.username} - - {log.action} - - {log.timestamp} - {log.name}{log.action}{log.timestamp}
- {/* Bouton de téléchargement des logs */}

-
-
-
- -
-

- {!verif - ? "Entrez les données de votre nouvel objet" - : "Êtes-vous sûr de ces données ?"} -

-
-
- - setNom(e.target.value)} - required - disabled={verif} - /> -
-
- - setDescription(e.target.value)} - required - disabled={verif} - /> -
- -
- - setType(e.target.value)} - required - disabled={verif} - /> -
- -
- - setLocalisation(e.target.value)} - required - disabled={verif} - /> -
- -
- -
- -
- - -
- -
-
- -
- - -
-

- {messRequete} -

-
+

);