diff --git a/Back-end/pom.xml b/Back-end/pom.xml index 2b51981..5d0ce82 100644 --- a/Back-end/pom.xml +++ b/Back-end/pom.xml @@ -49,7 +49,7 @@ io.agroal agroal-pool - 1.16 + 1.16 io.vertx 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 50b00d3..971c6e2 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -1,15 +1,14 @@ package com.example.starter; +import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.BodyHandler; import io.vertx.ext.web.handler.CorsHandler; -import io.vertx.sqlclient.Row; -import io.vertx.sqlclient.RowSet; -import io.vertx.sqlclient.Tuple; import io.vertx.core.http.HttpMethod; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; + import io.vertx.ext.web.Router; import io.vertx.ext.web.RoutingContext; import at.favre.lib.crypto.bcrypt.BCrypt; @@ -45,6 +44,12 @@ public class MainVerticle extends AbstractVerticle { router.route().handler(BodyHandler.create()); // Gestion des CORS + QueryObjects queryObjects = new QueryObjects(databaseService); + QueryWeatherData queryWeather = new QueryWeatherData(databaseService); + SetObjects setObjects = new SetObjects(databaseService); + // Create a Router + Router router = Router.router(vertx); + router.route().handler(BodyHandler.create()); router.route().handler(CorsHandler.create() .addOrigin("*") .allowedMethod(HttpMethod.GET) @@ -59,6 +64,12 @@ public class MainVerticle extends AbstractVerticle { router.post("/login").handler(this::handleLogin); // Route pour la connexion // Protéger toutes les routes commençant par "/api/" router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth)); + router.get("/objets").handler(queryObjects::getObjects); + router.get("/objet").handler(queryObjects::getParticularObject); + router.post("/modifObjet").handler(setObjects::setInfoObjet); + router.get("/wind").handler(queryWeather::getWindInfos); + router.get("/meteo").handler(queryWeather::getMeteoInfos); + router.post("/addObject").handler(setObjects::newObject); // Création du serveur HTTP vertx.createHttpServer() @@ -259,3 +270,5 @@ public class MainVerticle extends AbstractVerticle { } + +} \ No newline at end of file diff --git a/Back-end/src/main/java/com/example/starter/ProductHandler.java b/Back-end/src/main/java/com/example/starter/ProductHandler.java deleted file mode 100644 index 344bff1..0000000 --- a/Back-end/src/main/java/com/example/starter/ProductHandler.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.example.starter; - -public class ProductHandler { - -} diff --git a/Back-end/src/main/java/com/example/starter/QueryObjects.java b/Back-end/src/main/java/com/example/starter/QueryObjects.java new file mode 100644 index 0000000..fe186fc --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/QueryObjects.java @@ -0,0 +1,86 @@ +package com.example.starter; + +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import java.time.format.DateTimeFormatter; + +import io.vertx.ext.web.RoutingContext; + + +public class QueryObjects { + private DatabaseService databaseService; + public QueryObjects(DatabaseService dtbS){ + this.databaseService = dtbS; + } + + public void getObjects(RoutingContext context) { + databaseService.pool + .query("SELECT * FROM weather_objects;") + .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 -> { + context.response() + .putHeader("content-type", "application/json; charset=UTF-8") + .end(getInfosObjects(rows).encode()); + }); + } + + public void getParticularObject(RoutingContext context) { + String id = context.request().getParam("id"); + if (id == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Paramètre 'id' manquant").encode()); + return; + } + databaseService.pool + .preparedQuery("SELECT * 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("Erreur", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.size() == 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(getInfosObjects(rows).encode()); + }); + + } + + private JsonArray getInfosObjects(RowSet rows) { + JsonArray objects = new JsonArray(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); + for (Row row : rows) { + JsonObject object = new JsonObject() + .put("id", row.getInteger("id")) + .put("name", row.getString("name")) + .put("description", row.getString("description")) + .put("type", row.getString("type")) + .put("location", row.getString("location")) + .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")); + objects.add(object); + } + return objects; + } +} diff --git a/Back-end/src/main/java/com/example/starter/QueryWeatherData.java b/Back-end/src/main/java/com/example/starter/QueryWeatherData.java new file mode 100644 index 0000000..2f3ba4a --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/QueryWeatherData.java @@ -0,0 +1,99 @@ +package com.example.starter; + +import io.vertx.sqlclient.Row; +import io.vertx.sqlclient.RowSet; +import io.vertx.sqlclient.Tuple; + +import java.time.format.DateTimeFormatter; + +import io.vertx.core.json.JsonArray; +import io.vertx.core.json.JsonObject; + +import io.vertx.ext.web.RoutingContext; + +public class QueryWeatherData { + private DatabaseService databaseService; + + public QueryWeatherData(DatabaseService dtbS) { + this.databaseService = dtbS; + } + + public void getWindInfos(RoutingContext context) { + String id = context.request().getParam("id"); + if (id == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Paramètre 'id' manquant").encode()); + return; + } + + databaseService.pool + .preparedQuery("SELECT wind_speed,wind_direction,timestamp FROM weather_data WHERE station_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("Erreur", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.size() == 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((convertRowsToJson(rows)).encode()); + }); + } + public void getMeteoInfos(RoutingContext context) { + String id = context.request().getParam("id"); + if (id == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Paramètre 'id' manquant").encode()); + return; + } + + databaseService.pool + .preparedQuery("SELECT temperature,humidity,pressure,timestamp FROM weather_data WHERE station_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("Erreur", "Erreur de récupération de la BDD").encode()); + }) + .onSuccess(rows -> { + if (rows.size() == 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((convertRowsToJson(rows)).encode()); + }); + } + + private JsonArray convertRowsToJson(RowSet rows) { + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss"); + JsonArray jsonArray = new JsonArray(); + for (Row row : rows) { + JsonObject jsonObject = new JsonObject(); + for (int i = 0; i < row.size(); i++) { + String column = row.getColumnName(i); + if(column.compareTo("timestamp") == 0){ + jsonObject.put("timestamp",row.getLocalDateTime("timestamp").format(formatter)); + }else{ + jsonObject.put(column, row.getValue(column)); + } + } + jsonArray.add(jsonObject); + } + return jsonArray; + } +} \ No newline at end of file diff --git a/Back-end/src/main/java/com/example/starter/SetObjects.java b/Back-end/src/main/java/com/example/starter/SetObjects.java new file mode 100644 index 0000000..dd35436 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/SetObjects.java @@ -0,0 +1,89 @@ +package com.example.starter; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.sqlclient.Tuple; + +public class SetObjects { + private DatabaseService databaseService; + + public SetObjects(DatabaseService ddbs) { + this.databaseService = ddbs; + } + + public void setInfoObjet(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 description = body.getString("description"); + String type = body.getString("type"); + String location = body.getString("location"); + String status = body.getString("status"); + + databaseService.pool + .preparedQuery( + "UPDATE weather_objects SET description=?,type=?,location=?,status=?,last_update=CURRENT_TIMESTAMP WHERE id=?") + .execute(Tuple.of(description, type, location, status, 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", "Objet non trouvé").encode()); + return; + } + context.response() + .putHeader("content-type", "application/json: charset=UTF-8") + .end(new JsonObject().put("success", "L'objet à bien été mis à jour").encode()); + return; + }); + } + + public void newObject(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 name = body.getString("nom"); + String description = body.getString("description"); + String type = body.getString("type"); + String location = body.getString("location"); + String status = body.getString("status"); + databaseService.pool + .preparedQuery("INSERT INTO weather_objects (name,description,type,location,status) VALUES (?,?,?,?,?)") + .execute(Tuple.of(name,description,type,location,status)) + .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é ajouté").encode()); + return; + }); + + } + +} diff --git a/Front-end/package-lock.json b/Front-end/package-lock.json index d8b5823..33ac717 100644 --- a/Front-end/package-lock.json +++ b/Front-end/package-lock.json @@ -12,8 +12,11 @@ "axios": "^1.8.4", "lucide-react": "^0.427.0", "react": "^18.3.1", + "react-charts": "^3.0.0-beta.57", + "react-circle-progress-bar": "^0.1.4", "react-dom": "^18.3.1", - "react-router-dom": "^7.4.0" + "react-router-dom": "^7.4.0", + "recharts": "^2.15.1" }, "devDependencies": { "@eslint/js": "^9.9.1", @@ -262,6 +265,18 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", + "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", @@ -1302,6 +1317,69 @@ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", "license": "MIT" }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -1314,6 +1392,44 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/raf": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", + "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "17.0.85", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.85.tgz", + "integrity": "sha512-5oBDUsRDsrYq4DdyHaL99gE1AJCfuDhyxqF6/55fvvOIRkp1PpKuwJ+aMiGJR+GJt7YqMNclPROTHF20vY2cXA==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "^0.16", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "17.0.26", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.26.tgz", + "integrity": "sha512-Z+2VcYXJwOqQ79HreLU/1fyQ88eXSSFh6I3JdrEHQIfYSI0kCQpTGvOrbE6jFGGYXKsHuwY9tBa/w5Uo6KzrEg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", + "integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==", + "license": "MIT" + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -1656,6 +1772,15 @@ "node": ">= 6" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1742,6 +1867,127 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-delaunay": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-5.3.0.tgz", + "integrity": "sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w==", + "license": "ISC", + "dependencies": { + "delaunator": "4" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-color": "1 - 2" + } + }, + "node_modules/d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-scale": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.3.0.tgz", + "integrity": "sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "^2.3.0", + "d3-format": "1 - 2", + "d3-interpolate": "1.2.0 - 2", + "d3-time": "^2.1.1", + "d3-time-format": "2 - 3" + } + }, + "node_modules/d3-scale/node_modules/d3-time-format": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-3.0.0.tgz", + "integrity": "sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-time": "1 - 2" + } + }, + "node_modules/d3-shape": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-2.1.0.tgz", + "integrity": "sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1 - 2" + } + }, + "node_modules/d3-time": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-2.1.1.tgz", + "integrity": "sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ==", + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "2" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -1759,12 +2005,24 @@ } } }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, + "node_modules/delaunator": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-4.0.1.tgz", + "integrity": "sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==", + "license": "ISC" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1786,6 +2044,16 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "dev": true }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2090,12 +2358,27 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -2497,6 +2780,12 @@ "node": ">=0.8.19" } }, + "node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "license": "ISC" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2707,6 +2996,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2884,7 +3179,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3197,6 +3491,23 @@ "node": ">= 0.8.0" } }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3243,6 +3554,85 @@ "node": ">=0.10.0" } }, + "node_modules/react-charts": { + "version": "3.0.0-beta.57", + "resolved": "https://registry.npmjs.org/react-charts/-/react-charts-3.0.0-beta.57.tgz", + "integrity": "sha512-vqas7IQhsnDGcMxreGaWXvSIL3poEMoUBNltJrslz/+m0pI3QejBCszL1QrLNYQfOWXrbZADfedi/a+yWOQ7Hw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.14.6", + "@types/d3-array": "^3.0.1", + "@types/d3-scale": "^4.0.1", + "@types/d3-shape": "^3.0.1", + "@types/raf": "^3.4.0", + "@types/react": "^17.0.14", + "@types/react-dom": "^17.0.9", + "d3-array": "^2.12.1", + "d3-delaunay": "5.3.0", + "d3-scale": "^3.3.0", + "d3-shape": "^2.1.0", + "d3-time": "^2.1.1", + "d3-time-format": "^4.1.0", + "ts-toolbelt": "^9.6.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-circle-progress-bar": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/react-circle-progress-bar/-/react-circle-progress-bar-0.1.4.tgz", + "integrity": "sha512-2a47TDthNyUHJf8p1hv0wcTwIWnJBbEUfj/7dZcO+7BYd1W1sRC2t5x+SEWX9/1QT7hhf4t8ppcLaG0XrdwwgQ==", + "license": "MIT", + "dependencies": { + "react": "^16.8.6", + "react-dom": "^16.8.6" + } + }, + "node_modules/react-circle-progress-bar/node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-circle-progress-bar/node_modules/react-dom": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + }, + "peerDependencies": { + "react": "^16.14.0" + } + }, + "node_modules/react-circle-progress-bar/node_modules/scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -3255,6 +3645,12 @@ "react": "^18.3.1" } }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, "node_modules/react-refresh": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", @@ -3304,6 +3700,37 @@ "react-dom": ">=18" } }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -3325,6 +3752,44 @@ "node": ">=8.10.0" } }, + "node_modules/recharts": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz", + "integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -3709,6 +4174,12 @@ "node": ">=0.8" } }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3727,6 +4198,12 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/ts-toolbelt": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz", + "integrity": "sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w==", + "license": "Apache-2.0" + }, "node_modules/turbo-stream": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", @@ -3790,6 +4267,101 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/victory-vendor/node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/victory-vendor/node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/vite": { "version": "5.4.15", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.15.tgz", diff --git a/Front-end/package.json b/Front-end/package.json index 5791d1c..da15ec6 100644 --- a/Front-end/package.json +++ b/Front-end/package.json @@ -13,8 +13,11 @@ "axios": "^1.8.4", "lucide-react": "^0.427.0", "react": "^18.3.1", + "react-charts": "^3.0.0-beta.57", + "react-circle-progress-bar": "^0.1.4", "react-dom": "^18.3.1", - "react-router-dom": "^7.4.0" + "react-router-dom": "^7.4.0", + "recharts": "^2.15.1" }, "devDependencies": { "@eslint/js": "^9.9.1", diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index 36e3226..5851df6 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -6,6 +6,7 @@ import Gestion from "./pages/Gestion/Gestion.jsx"; import Header from "./components/Header.jsx"; import ObjectManagement from "./pages/Gestion/ObjectManagement.jsx"; import Objet from "./pages/Gestion/Objet.jsx"; +import AddObject from "./pages/Gestion/AddObject.jsx"; import Signup from './pages/Signup.jsx'; import Login from './pages/Login.jsx'; @@ -23,6 +24,7 @@ function App() { } /> } /> } /> + } /> diff --git a/Front-end/src/components/AlertInactive.jsx b/Front-end/src/components/AlertInactive.jsx new file mode 100644 index 0000000..917694b --- /dev/null +++ b/Front-end/src/components/AlertInactive.jsx @@ -0,0 +1,22 @@ +import React, {useState} from "react"; +import { TriangleAlert,X } from "lucide-react"; + +function AlertInactive({affAlert,setAffAlert}) { + return ( + (affAlert&&( +
+ + + + +

+ Cet objet peut être inactif dû à son manque de données. Vous pouvez le + rendre inactif en appuyant ici. +

+
+ ))); +} + +export default AlertInactive; \ No newline at end of file diff --git a/Front-end/src/components/BatterieInfo.jsx b/Front-end/src/components/BatterieInfo.jsx new file mode 100644 index 0000000..642fe31 --- /dev/null +++ b/Front-end/src/components/BatterieInfo.jsx @@ -0,0 +1,27 @@ +import React from "react"; +import { Battery } from "lucide-react"; +import Progress from "react-circle-progress-bar"; + +function BatterieInfo({ object }) { + return ( +
+
+
+ +
+

+ Etat de la batterie +

+
+
+ +

+ Type de batterie :{" "} + {object.type_batterie} +

+
+
+ ); +} + +export default BatterieInfo; diff --git a/Front-end/src/components/BoutonGraphique.jsx b/Front-end/src/components/BoutonGraphique.jsx new file mode 100644 index 0000000..932725f --- /dev/null +++ b/Front-end/src/components/BoutonGraphique.jsx @@ -0,0 +1,27 @@ +import React, {useRef} from "react"; +import { ChartLine } from "lucide-react"; +function BoutonGraphique({ TypeAff, setAffichage,graphCible}) { + const handleClick = (newAffichage) =>{ + setAffichage(newAffichage); + if(graphCible.current){ + graphCible.current.scrollIntoView({ behavior: "smooth" }); + } + }; + return !TypeAff ? ( + + ) : ( + + ); +} + +export default BoutonGraphique; diff --git a/Front-end/src/components/InfoObject.jsx b/Front-end/src/components/InfoObject.jsx new file mode 100644 index 0000000..9c16424 --- /dev/null +++ b/Front-end/src/components/InfoObject.jsx @@ -0,0 +1,46 @@ +import React from "react"; +import { Info } from "lucide-react"; + +function InfoObject({ object,defafficherModif }) { + return ( +
+
+
+ +
+

Informations

+
+
+

Description :

+

{object.description}

+
+ +
+

Type :

+

{object.type}

+
+ +
+

Localisation :

+

{object.location}

+
+ +
+

Status :

+

{object.status}

+
+ +
+

+ Derniere mise à jour : +

+

{object.last_update}

+
+ +
+ ); +} + +export default InfoObject; diff --git a/Front-end/src/components/MeteoGraph.jsx b/Front-end/src/components/MeteoGraph.jsx new file mode 100644 index 0000000..ee957f4 --- /dev/null +++ b/Front-end/src/components/MeteoGraph.jsx @@ -0,0 +1,90 @@ +import React, { useEffect, useState } from "react"; +import { + LineChart, + Line, + XAxis, + YAxis, + CartesianGrid, + Tooltip, + Legend, + ResponsiveContainer, + ReferenceLine, +} from "recharts"; + +import { Wind } from "lucide-react"; +import axios from "axios"; +import { API_BASE_URL } from "../config"; + +function MeteoGraph({ object, categorie, Logo }) { + const [rawData, setRawData] = useState([]); + const identifiant = object.id; + useEffect(() => { + axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => { + setRawData(response.data); + }); + }, [object]); + + function getAvg() { + let moyenne = 0; + rawData.forEach((element) => { + if(element){ + moyenne += element[categorie]; + } + }); + return moyenne / rawData.length; + } + return ( +
+
+
+ +
+ {categorie === "temperature" ? ( +

+ Historique de la température +

+ ) : categorie === "humidity" ? ( +

+ Historique de l'humidité +

+ ) : ( +

+ Historique de la pression +

+ )} +
+ + + + + + + + + + + +
+ ); +} + +export default MeteoGraph; diff --git a/Front-end/src/components/MeteoInfos.jsx b/Front-end/src/components/MeteoInfos.jsx new file mode 100644 index 0000000..1e2604b --- /dev/null +++ b/Front-end/src/components/MeteoInfos.jsx @@ -0,0 +1,130 @@ +import { Thermometer, Sun, CircleGauge, Droplet } from "lucide-react"; +import React, { useEffect, useState } from "react"; + +import axios from "axios"; +import { API_BASE_URL } from "../config"; +import BoutonGraphique from "./BoutonGraphique"; +import AlertInactive from "./AlertInactive"; + +function MeteoInfos({ + object, + defAffTempGraph, + AffTempGraph, + defAffPressionGraph, + AffPressionGraph, + defAffHumiditeGraph, + AffHumiditeGraph, + graphCible +}) { + const [rawData, setRawData] = useState([]); + const [AffAlert,setAffAlert] = useState(false); + const identifiant = object.id; + + useEffect(() => { + axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => { + setRawData(response.data); + if(rawData.length <5){ + setAffAlert(true); + } + }); + }, [object]); + + const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null; + console.log(rawData.length); + return ( +
+ {(AffAlert&&(object.status==="active")) && ( + + )} +
+
+ +
+

Météo actuelle

+
+ {lastData ? ( +
+ {lastData.temperature && ( +
+
+
+
+ +
+
+

+ Température +

+

+ {lastData.temperature} °C +

+
+
+ +
+
+ )} + {lastData.pressure && ( +
+
+
+
+ +
+
+

+ Pression +

+

+ {lastData.pressure} hPa +

+
+
+ +
+
+ )} + {lastData.humidity && ( +
+
+
+
+ +
+
+

+ Humidité +

+

+ {lastData.humidity} % +

+
+
+ +
+
+ )} +

+ Dernier enregistrement : {lastData.timestamp} +

+
+ ) : ( +

Chargement des données...

+ )} +
+ ); +} + +export default MeteoInfos; diff --git a/Front-end/src/components/ModifObject.jsx b/Front-end/src/components/ModifObject.jsx new file mode 100644 index 0000000..b3658d1 --- /dev/null +++ b/Front-end/src/components/ModifObject.jsx @@ -0,0 +1,158 @@ +import React, { useState } from "react"; +import { Info } from "lucide-react"; +import axios from "axios"; +import { API_BASE_URL } from "../config"; + +function ModifObject({ object, defafficherModif }) { + const [description, setDescription] = useState(object.description || ""); + const [type, setType] = useState(object.type || ""); + const [location, setLocalisation] = useState(object.location || ""); + const [status, setStatus] = useState(object.status || "inactive"); + const [isActive, setActive] = useState(object.status === "active"); + + function handleSubmit(event) { + event.preventDefault(); // Empêche le rechargement de la page + axios + .post(`${API_BASE_URL}/modifObjet`, { + id: object.id, + description, + type, + location, + status, + }) + .then((response) => { + console.log("Modification réussie :", response.data); + }) + .catch((error) => { + console.error("Erreur lors de la modification :", error); + }); + defafficherModif(false); + window.location.reload(); + } + + function handleCancel() { + defafficherModif(false); + } + + function handleStatusChange() { + setActive((prevIsActive) => { + const newIsActive = !prevIsActive; + setStatus(newIsActive ? "active" : "inactive"); + return newIsActive; + }); + } + + return ( +
+
+
+ +
+

+ Modifier les infos +

+
+
+ + setDescription(e.target.value)} + required + /> +
+ +
+ + setType(e.target.value)} + required + /> +
+ +
+ + setLocalisation(e.target.value)} + required + /> +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ + +
+
+ ); +} + +export default ModifObject; diff --git a/Front-end/src/components/WindGraph.jsx b/Front-end/src/components/WindGraph.jsx new file mode 100644 index 0000000..575d8da --- /dev/null +++ b/Front-end/src/components/WindGraph.jsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState} from "react"; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; + +import { Wind } from "lucide-react"; +import axios from "axios"; +import { API_BASE_URL } from "../config"; + +function WindGraph({ object }) { + const [rawData, setRawData] = useState([]); + const identifiant = object.id; + useEffect(() => { + axios.get(`${API_BASE_URL}/wind?id=${identifiant}`).then((response) => { + setRawData(response.data); + }); + }, [object]); + const CustomTooltip = ({ payload, label, active }) => { + if (active && payload && payload.length) { + const { wind_speed, timestamp,wind_direction } = payload[0].payload; + return ( +
+

Date: {timestamp}

+

Vitesse du vent: {wind_speed} km/h

+

Direction du vent: {wind_direction}

+
+ ); + } + + return null; // Si aucun point n'est survolé + }; + return ( +
+
+
+ +
+

Historique du vent

+
+ + + + + + }/> + + + + +
+ ); +} + +export default WindGraph; diff --git a/Front-end/src/components/WindInfo.jsx b/Front-end/src/components/WindInfo.jsx new file mode 100644 index 0000000..ac479d5 --- /dev/null +++ b/Front-end/src/components/WindInfo.jsx @@ -0,0 +1,67 @@ +import { Wind } from "lucide-react"; +import React, { useEffect, useState } from "react"; + +import axios from "axios"; +import { API_BASE_URL } from "../config"; +import BoutonGraphique from "./BoutonGraphique"; + +function WindInfo({ object, defAffWindGraph, AffWindGraph }) { + const [rawData, setRawData] = useState([]); + const identifiant = object.id; + useEffect(() => { + axios.get(`${API_BASE_URL}/wind?id=${identifiant}`).then((response) => { + setRawData(response.data); + }); + }, [object]); + + const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null; + + return ( +
+
+
+ +
+

Vent actuel

+
+ {lastData ? ( +
+ Wind Direction +

+ {lastData.wind_direction} +

+
+
+
+
+ +
+
+

Valeur

+

+ {lastData.wind_speed} Km/h +

+
+
+ +
+
+

+ Dernier enregistrement : {lastData.timestamp} +

+
+ ) : ( +

Chargement des données...

+ )} +
+ ); +} + +export default WindInfo; diff --git a/Front-end/src/config.js b/Front-end/src/config.js new file mode 100644 index 0000000..5c2983f --- /dev/null +++ b/Front-end/src/config.js @@ -0,0 +1 @@ +export const API_BASE_URL = 'http://localhost:8888'; \ No newline at end of file diff --git a/Front-end/src/img/Est.png b/Front-end/src/img/Est.png new file mode 100644 index 0000000..96a8101 Binary files /dev/null and b/Front-end/src/img/Est.png differ diff --git a/Front-end/src/img/Nord-Est.png b/Front-end/src/img/Nord-Est.png new file mode 100644 index 0000000..108fc9a Binary files /dev/null and b/Front-end/src/img/Nord-Est.png differ diff --git a/Front-end/src/img/Nord-Ouest.png b/Front-end/src/img/Nord-Ouest.png new file mode 100644 index 0000000..6d58631 Binary files /dev/null and b/Front-end/src/img/Nord-Ouest.png differ diff --git a/Front-end/src/img/Nord.png b/Front-end/src/img/Nord.png new file mode 100644 index 0000000..8c47efa Binary files /dev/null and b/Front-end/src/img/Nord.png differ diff --git a/Front-end/src/img/NotreMission.png b/Front-end/src/img/NotreMission.png new file mode 100644 index 0000000..f51ebc9 Binary files /dev/null and b/Front-end/src/img/NotreMission.png differ diff --git a/Front-end/src/img/Ouest.png b/Front-end/src/img/Ouest.png new file mode 100644 index 0000000..7b544a9 Binary files /dev/null and b/Front-end/src/img/Ouest.png differ diff --git a/Front-end/src/img/Sud-Est.png b/Front-end/src/img/Sud-Est.png new file mode 100644 index 0000000..39d0632 Binary files /dev/null and b/Front-end/src/img/Sud-Est.png differ diff --git a/Front-end/src/img/Sud-Ouest.png b/Front-end/src/img/Sud-Ouest.png new file mode 100644 index 0000000..2840f64 Binary files /dev/null and b/Front-end/src/img/Sud-Ouest.png differ diff --git a/Front-end/src/img/Sud.png b/Front-end/src/img/Sud.png new file mode 100644 index 0000000..d3e5bd7 Binary files /dev/null and b/Front-end/src/img/Sud.png differ diff --git a/Front-end/src/img/fr-alert.webp b/Front-end/src/img/fr-alert.webp new file mode 100644 index 0000000..d1ab54b Binary files /dev/null and b/Front-end/src/img/fr-alert.webp differ diff --git a/Front-end/src/img/gestioniot.png b/Front-end/src/img/gestioniot.png new file mode 100644 index 0000000..d2661ab Binary files /dev/null and b/Front-end/src/img/gestioniot.png differ diff --git a/Front-end/src/img/iotmeteo.jpg b/Front-end/src/img/iotmeteo.jpg new file mode 100644 index 0000000..19a2683 Binary files /dev/null and b/Front-end/src/img/iotmeteo.jpg differ diff --git a/Front-end/src/img/precisionfiable.jpg b/Front-end/src/img/precisionfiable.jpg new file mode 100644 index 0000000..b861940 Binary files /dev/null and b/Front-end/src/img/precisionfiable.jpg differ diff --git a/Front-end/src/img/surveillancemeteo.webp b/Front-end/src/img/surveillancemeteo.webp new file mode 100644 index 0000000..976fac5 Binary files /dev/null and b/Front-end/src/img/surveillancemeteo.webp differ diff --git a/Front-end/src/img/surveillancetempsreel.jpg b/Front-end/src/img/surveillancetempsreel.jpg new file mode 100644 index 0000000..07405a2 Binary files /dev/null and b/Front-end/src/img/surveillancetempsreel.jpg differ diff --git a/Front-end/src/pages/About.jsx b/Front-end/src/pages/About.jsx index 892f66e..416ee62 100644 --- a/Front-end/src/pages/About.jsx +++ b/Front-end/src/pages/About.jsx @@ -1,11 +1,168 @@ -import React from 'react'; +import React from "react"; function About() { - return ( -
-

A propos

+ return ( +
+
+
+ {/* Grille principale */} +
+ {/* Section Notre mission */} +
+

+ Notre mission +

+

+ Notre mission est de fournir une solution complète et innovante + pour la surveillance climatique et environnementale du + territoire français. En combinant des prévisions météorologiques + de haute qualité avec une gestion efficace des objets connectés, + nous visons à offrir une plateforme centralisée permettant de + surveiller en temps réel les conditions météorologiques locales, + tout en facilitant l'analyse des données collectées par des + objets connectés déployés à travers le pays. +

+
+ Notre mission + + {/* Section Qui sommes-nous */} + IoT et météo +
+

+ Qui sommes-nous ? +

+

+ Nous sommes une équipe de passionnés de technologie, + d’innovation et d’environnement. Nous croyons fermement que la + combinaison de la donnée météorologique en temps réel et de + l’Internet des Objets (IoT) peut avoir un impact majeur sur la + gestion des territoires. Que ce soit pour les collectivités + locales, les entreprises ou les acteurs publics, notre + plateforme offre les outils nécessaires pour une gestion + proactive et réactive de l’environnement. +

+
+ + {/* Section Notre Vision */} +
+

+ Notre Vision +

+

+ Dans un monde où les conditions climatiques évoluent rapidement, + il est essentiel de pouvoir anticiper et réagir efficacement + face aux phénomènes météorologiques. Grâce à nos objets + connectés et à notre interface intuitive, nous permettons aux + utilisateurs de suivre les conditions en temps réel et d’agir en + conséquence. De la gestion des risques climatiques à la + planification urbaine, notre plateforme aide les décideurs à + prendre des décisions éclairées basées sur des données fiables + et locales. +

+
+ Surveillance météo +
+ + {/* Section Objectifs */} +
+

+ Les Objectifs de Notre Plateforme +

+
+ {/* Objectif 1 */} +
+ Surveillance en temps réel +
+

+ Grâce à nos objets connectés, nous collectons des données + météorologiques locales, permettant une surveillance + continue des conditions climatiques sur tout le territoire + français. +

+
+

+ Surveillance en temps réel +

+
+ + {/* Objectif 2 */} +
+ Précision fiable +
+

+ En utilisant les meilleures technologies de prévision + météorologique, nous vous fournissons des prévisions + précises, qu’il s’agisse de la température, de la vitesse du + vent ou de la qualité de l’air. +

+
+

Prédiction fiable

+
+ + {/* Objectif 3 */} +
+ Gestion IoT +
+

+ Nous permettons aux utilisateurs de gérer facilement leurs + objets connectés (stations météo, capteurs, etc.) à travers + une interface simple, tout en offrant un suivi en temps réel + de leur statut et de leurs données. +

+
+

+ Gestion des objets connectés +

+
+ + {/* Objectif 4 */} +
+ Réponse rapide +
+

+ Notre plateforme vous envoie des alertes instantanées + concernant les phénomènes météorologiques extrêmes, vous + permettant de prendre des décisions rapides et adaptées. +

+
+

+ Réponse rapide aux alertes climatiques +

+
+
+
- ); +
+
+ ); } -export default About; \ No newline at end of file +export default About; diff --git a/Front-end/src/pages/Gestion/AddObject.jsx b/Front-end/src/pages/Gestion/AddObject.jsx new file mode 100644 index 0000000..8881166 --- /dev/null +++ b/Front-end/src/pages/Gestion/AddObject.jsx @@ -0,0 +1,220 @@ +import React, { useState } from "react"; +import { BadgePlus } from "lucide-react"; +import axios from "axios"; +import { API_BASE_URL } from "../../config"; + +function AddObject() { + const [description, setDescription] = useState(""); + const [type, setType] = useState(""); + const [location, setLocalisation] = useState(""); + 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, + }) + .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(""); + setDescription(""); + setType(""); + setLocalisation(""); + setActive(true); + } + function handleCancel() { + if (verif) { + setVerif(false); + } else { + resetForm(); + } + } + + function handleStatusChange() { + setActive((prevIsActive) => { + const newIsActive = !prevIsActive; + setStatus(newIsActive ? "active" : "inactive"); + return newIsActive; + }); + } + + return ( +
+
+
+

+ 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} + /> +
+ +
+ +
+ +
+ + +
+ +
+
+ +
+ + +
+

+ {messRequete} +

+
+
+
+ ); +} + +export default AddObject; diff --git a/Front-end/src/pages/Gestion/Gestion.jsx b/Front-end/src/pages/Gestion/Gestion.jsx index b3d1e9b..dce1545 100644 --- a/Front-end/src/pages/Gestion/Gestion.jsx +++ b/Front-end/src/pages/Gestion/Gestion.jsx @@ -10,9 +10,8 @@ import { RadioTower, Binoculars, Settings, - ChartArea, + BadgePlus, } from "lucide-react"; - function Gestion() { return (
@@ -22,84 +21,43 @@ function Gestion() { Bienvenue dans le module Gestion.

- Dans ce module, vous allez pouvoir gerer les objets connectés de - l'hopital. + Ce module vous permet de gérer les capteur et stations connectés de France de manière simple et efficace.

- {/* Features Grid */} -
+

- Gestion des objets connectés + Consulter les objets connectés météorologiques

- Découvrez les meilleurs endroits de votre ville, qu'il s'agisse de - restaurants, de parcs ou de lieux culturels. + Accédez aux données en temps réel des objets connectés météorologiques, modifiez leurs paramètres et consultez l'historique des mesures.

- Explorer les lieux + Explorer les objets
- +

- Configurer des services + Ajouter un nouvel objet connecté

- Découvrez les meilleurs endroits de votre ville, qu'il s'agisse de - restaurants, de parcs ou de lieux culturels. + Intégrez facilement un nouvel objet connecté en renseignant ses informations et en configurant ses paramètres pour une gestion optimale.

- Explorer les lieux - -
-
-
- -
-

- Surveillance et optimisation des ressources -

-

- Restez informé des derniers événements, festivals et - rassemblements communautaires dans votre région. -

- - Voir les événements - -
- -
-
- -
-

- Rapports statistiques -

-

- Accédez en temps réel aux horaires et aux itinéraires des bus, des - trains et des autres transports publics. -

- - Vérifier les horaires + Ajouter un objet
diff --git a/Front-end/src/pages/Gestion/ObjectManagement.jsx b/Front-end/src/pages/Gestion/ObjectManagement.jsx index 5ed5082..a440258 100644 --- a/Front-end/src/pages/Gestion/ObjectManagement.jsx +++ b/Front-end/src/pages/Gestion/ObjectManagement.jsx @@ -1,29 +1,31 @@ import React from "react"; -import { - Search, - MapPin, - Calendar, - Bus, - ArrowRight, - LogIn, - UserPlus, - RadioTower, - Binoculars, - Settings, - ChartArea, -} from "lucide-react"; +import { Search, ArrowRight, RadioTower,Plus } from "lucide-react"; import { useEffect, useState } from "react"; import axios from "axios"; -import { useFetcher } from "react-router-dom"; function ObjectManagement() { const [searchQuery, setSearchQuery] = useState(""); - const [activeFilter, setActiveFilter] = useState("all"); + const [activeFilter, setActiveFilter] = useState(""); const [objects, setObjects] = useState([]); + const [nbAffObject,setnbAffObject] = useState(6); + const filteredDATA = objects.filter((node) => { + const matchesSearchQuery = + searchQuery === "" || + node.name.toLowerCase().includes(searchQuery.toLowerCase()) || + node.description.toLowerCase().includes(searchQuery.toLowerCase()); + + const matchesTag = + activeFilter === "" || + node.name.toLowerCase().includes(activeFilter.toLowerCase()) || + node.description.includes(activeFilter.toLowerCase()) || + (activeFilter === "Active" && node.status.toLowerCase() === "active") || + (activeFilter === "Inactive" && node.status.toLowerCase() === "inactive"); + return matchesSearchQuery && matchesTag; + }); + useEffect(() => { axios.get("http://localhost:8888/objets").then((response) => { setObjects(response.data); - console.log(response.data); }); }, []); return ( @@ -50,9 +52,9 @@ function ObjectManagement() {
- + +
- {objects.map(object =>( -
+ {filteredDATA.length === 0 ? ( +

Aucun objet trouvé

+ ) : ( + filteredDATA.slice(0,nbAffObject).map((object) => ( +
+ {object.status === "active" ? ( +
+ + + + +
+ ) : ( +
+ + + +
+ )} +
- +
-

- {object.name} -

-

- {object.description} -

+

{object.name}

+

{object.description}

- Plus d'infos + Plus d'infos -
- ))} +
+ )) + )}
+ {(nbAffObject + + + + )} ); diff --git a/Front-end/src/pages/Gestion/Objet.jsx b/Front-end/src/pages/Gestion/Objet.jsx index 38a362b..c076b81 100644 --- a/Front-end/src/pages/Gestion/Objet.jsx +++ b/Front-end/src/pages/Gestion/Objet.jsx @@ -1,58 +1,117 @@ import React from "react"; -import { - Search, - MapPin, - Calendar, - Bus, - ArrowRight, - LogIn, - UserPlus, - RadioTower, - Binoculars, - Settings, - ChartArea, -} from "lucide-react"; -import { useEffect, useState } from "react"; -import axios from "axios"; -import { useFetcher } from "react-router-dom"; +import { Thermometer, CircleGauge, Droplet } from "lucide-react"; +import { useEffect, useState, useRef} from "react"; +import axios from "axios"; +import { API_BASE_URL } from "../../config"; + +import InfoObjet from "../../components/InfoObject"; +import ModifObject from "../../components/ModifObject"; +import WindGraph from "../../components/WindGraph"; +import WindInfo from "../../components/WindInfo"; +import MeteoInfos from "../../components/MeteoInfos"; +import MeteoGraph from "../../components/MeteoGraph"; +import BatterieInfo from "../../components/BatterieInfo"; function Objet() { const identifiant = new URLSearchParams(window.location.search).get("id"); const [searchQuery, setSearchQuery] = useState(""); const [activeFilter, setActiveFilter] = useState("all"); const [object, setObject] = useState({}); + const [graphStates, setGraphStates] = useState({ + wind:false, + temperature:false, + pressure:false, + humidity:false, + }) + const [afficherModif, defafficherModif] = useState(false); + const [AffWindGraph, defAffWindGraph] = useState(false); + const [AffTempGraph, defAffTempGraph] = useState(false); + const [AffPressionGraph, defAffPressionGraph] = useState(false); + const [AffHumiditeGraph, defAffHumideGraph] = useState(false); + const tempGraphRef = useRef(null); + const pressureGraphRef = useRef(null); + const humidityGraphRef = useRef(null); + const windGraphRef = useRef(null); + useEffect(() => { - axios.get(`http://localhost:8888/objet?id=${identifiant}`).then((response) => { + axios.get(`${API_BASE_URL}/objet?id=${identifiant}`).then((response) => { setObject(response.data[0]); - console.log(response.data); }); }, [identifiant]); return (
-
-
-

- {object.name} +
+
+

+ Tableau de bord - {object.name}

-
-
-
- -
-

{object.name}

-

{object.description}

- - Plus d'infos - -
+
+ {!afficherModif ? ( + + ) : ( + + )} + + {object && object.id ? ( + + ) : ( +

Chargement des données...

+ )} + {object && object.id ? ( + + ) : ( +

Chargement des données...

+ )} +
+ {AffWindGraph && + (object && object.id ? ( + + ) : ( +

Chargement des données...

+ ))} + {AffTempGraph && + (object && object.id ? ( + + ) : ( +

Chargement des données...

+ ))} + {AffPressionGraph && + (object && object.id ? ( + + ) : ( +

Chargement des données...

+ ))} + {AffHumiditeGraph && + (object && object.id ? ( + + ) : ( +

Chargement des données...

+ ))}
); diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index dfa1187..50b517e 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -1,18 +1,117 @@ import React, { useState } from 'react'; function Home() { - const token = localStorage.getItem("token"); - return ( - -
- {token ? ( + const [searchQuery, setSearchQuery] = useState(''); + const [activeFilter, setActiveFilter] = useState('all'); + const [name, setName] = useState([]); + return ( +
+
+
+

+ Bienvenue dans ta ville intelligente.

+ {token ? ( <>Home ):(

Non connecté

- )} -
- ) + )} +

+ Découvrez tout ce que votre ville a à offrir - des événements locaux aux horaires de transport, le tout en un seul endroit. +

+
+ +
+
+ + setSearchQuery(e.target.value)} + /> +
+
+ + + + +
+
+ + {/* Features Grid */} +
+
+
+ +
+

Points d'intérêt

+

+ Découvrez les meilleurs endroits de votre ville, qu'il s'agisse de restaurants, de parcs ou de lieux culturels. +

+ + Explorer les lieux + +
+ +
+
+ +
+

Evenements locaux

+

+ Restez informé des derniers événements, festivals et rassemblements communautaires dans votre région. +

+ + Voir les événements + +
+ +
+
+ +
+

Transports publics

+

+ Accédez en temps réel aux horaires et aux itinéraires des bus, des trains et des autres transports publics. +

+ + Vérifier les horaires + +
+
+
+
+ ); } export default Home; \ No newline at end of file