Ajout de la page d'info d'un objet
@ -1,17 +1,12 @@
|
||||
package com.example.starter;
|
||||
|
||||
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;
|
||||
|
||||
public class MainVerticle extends AbstractVerticle {
|
||||
private DatabaseService databaseService;
|
||||
@ -19,17 +14,22 @@ public class MainVerticle extends AbstractVerticle {
|
||||
@Override
|
||||
public void start(Promise<Void> startPromise) throws Exception {
|
||||
databaseService = new DatabaseService(vertx);
|
||||
QueryObjects queryObjects = new QueryObjects(databaseService);
|
||||
QueryWeatherData queryWeather = new QueryWeatherData(databaseService);
|
||||
// Create a Router
|
||||
Router router = Router.router(vertx);
|
||||
router.route().handler(BodyHandler.create());
|
||||
router.route().handler(CorsHandler.create()
|
||||
.addOrigin("*") // Allow all origins
|
||||
.allowedMethod(HttpMethod.GET) // Allow GET requests
|
||||
.allowedMethod(HttpMethod.POST) // Allow POST requests
|
||||
.allowedHeader("Content-Type") // Allow Content-Type header
|
||||
.allowedHeader("Authorization"));
|
||||
router.get("/objets").handler(this::getObjects);
|
||||
router.get("/objet").handler(this::getParticularObject);
|
||||
// Create the HTTP server
|
||||
router.get("/objets").handler(queryObjects::getObjects);
|
||||
router.get("/objet").handler(queryObjects::getParticularObject);
|
||||
router.post("/modifObjet").handler(queryObjects::setInfoObjet);
|
||||
router.get("/wind").handler(queryWeather::getWindInfos);
|
||||
router.get("/meteo").handler(queryWeather::getMeteoInfos);
|
||||
|
||||
vertx.createHttpServer()
|
||||
.requestHandler(router)
|
||||
@ -43,65 +43,4 @@ public class MainVerticle extends AbstractVerticle {
|
||||
startPromise.fail(throwable);
|
||||
});
|
||||
}
|
||||
|
||||
private 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());
|
||||
});
|
||||
}
|
||||
private 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<Row> rows){
|
||||
JsonArray objects = new JsonArray();
|
||||
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"));
|
||||
objects.add(object);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
package com.example.starter;
|
||||
|
||||
public class ProductHandler {
|
||||
|
||||
}
|
||||
124
Back-end/src/main/java/com/example/starter/QueryObjects.java
Normal file
@ -0,0 +1,124 @@
|
||||
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<Row> 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"));
|
||||
objects.add(object);
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Row> 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;
|
||||
}
|
||||
}
|
||||
526
Front-end/package-lock.json
generated
@ -12,8 +12,10 @@
|
||||
"axios": "^1.8.4",
|
||||
"lucide-react": "^0.427.0",
|
||||
"react": "^18.3.1",
|
||||
"react-charts": "^3.0.0-beta.57",
|
||||
"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 +264,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 +1316,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 +1391,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",
|
||||
@ -1657,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",
|
||||
@ -1743,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",
|
||||
@ -1760,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",
|
||||
@ -1787,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",
|
||||
@ -2091,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",
|
||||
@ -2498,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",
|
||||
@ -2708,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",
|
||||
@ -2885,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"
|
||||
}
|
||||
@ -3198,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",
|
||||
@ -3244,6 +3554,36 @@
|
||||
"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-dom": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
@ -3256,6 +3596,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",
|
||||
@ -3305,6 +3651,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",
|
||||
@ -3326,6 +3703,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",
|
||||
@ -3710,6 +4125,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",
|
||||
@ -3728,6 +4149,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",
|
||||
@ -3791,6 +4218,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",
|
||||
|
||||
@ -13,8 +13,10 @@
|
||||
"axios": "^1.8.4",
|
||||
"lucide-react": "^0.427.0",
|
||||
"react": "^18.3.1",
|
||||
"react-charts": "^3.0.0-beta.57",
|
||||
"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",
|
||||
|
||||
21
Front-end/src/components/BoutonGraphique.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from "react";
|
||||
import { ChartLine } from "lucide-react";
|
||||
function BoutonGraphique({ TypeAff, setAffichage }) {
|
||||
return !TypeAff ? (
|
||||
<button
|
||||
className="bg-blue-200 py-2 my-2 px-4 rounded-full mr-2"
|
||||
onClick={() => setAffichage(true)}
|
||||
>
|
||||
<ChartLine className="text-red-500" size={24} />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-blue-400 py-2 my-2 px-4 rounded-full mr-2"
|
||||
onClick={() => setAffichage(false)}
|
||||
>
|
||||
<ChartLine className="text-red-500" size={24} />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default BoutonGraphique;
|
||||
@ -4,22 +4,32 @@ import { LogIn, UserPlus } from "lucide-react";
|
||||
function Header() {
|
||||
return (
|
||||
<header className="bg-white shadow-sm">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
|
||||
<div className="flex justify-between items-center">
|
||||
<h1 className="text-2xl font-bold text-indigo-600">Hopital de Pau</h1>
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4 flex justify-between items-center">
|
||||
{/* Logo */}
|
||||
<h1 className="text-2xl font-bold text-indigo-600">VigiMétéo</h1>
|
||||
|
||||
{/* Navigation */}
|
||||
<nav>
|
||||
<ul>
|
||||
<ul className="flex gap-6 text-gray-600">
|
||||
<li>
|
||||
<a href="/">Accueil</a>
|
||||
<a href="/" className="hover:text-indigo-600">
|
||||
Accueil
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/about">A propos</a>
|
||||
<a href="/about" className="hover:text-indigo-600">
|
||||
À propos
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/gestion">Gestion</a>
|
||||
<a href="/gestion" className="hover:text-indigo-600">
|
||||
Gestion
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-4">
|
||||
<button className="flex items-center gap-2 text-gray-600 hover:text-indigo-600">
|
||||
<LogIn size={20} />
|
||||
@ -31,7 +41,6 @@ function Header() {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
48
Front-end/src/components/InfoObject.jsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from "react";
|
||||
import { Info } from "lucide-react";
|
||||
|
||||
function InfoObject({ object,defafficherModif }) {
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Info className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">Informations</h1>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Description :</p>
|
||||
<p className="text-gray-600">{object.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Type :</p>
|
||||
<p className="text-gray-600">{object.type}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Localisation :</p>
|
||||
<p className="text-gray-600">{object.location}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Status :</p>
|
||||
<p className="text-gray-600">{object.status}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">
|
||||
Derniere mise à jour :
|
||||
</p>
|
||||
<p className="text-gray-600">{object.last_update}</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<a className="text-blue-500 hover:cursor-pointer" onClick={(()=>defafficherModif(true))}>Modifier ces infos</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default InfoObject;
|
||||
91
Front-end/src/components/MeteoGraph.jsx
Normal file
@ -0,0 +1,91 @@
|
||||
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;
|
||||
}
|
||||
console.log(getAvg());
|
||||
return (
|
||||
<div
|
||||
key={object.id}
|
||||
className="bg-white mb-6 p-6 rounded-xl min-w-5xl"
|
||||
style={{ width: "100%", height: "400px" }}
|
||||
>
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Logo className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
{categorie === "temperature" ? (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
Historique de la température
|
||||
</h1>
|
||||
) : categorie === "humidity" ? (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
Historique de l'humidité
|
||||
</h1>
|
||||
) : (
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">
|
||||
Historique de la pression
|
||||
</h1>
|
||||
)}
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart
|
||||
width={500}
|
||||
height={300}
|
||||
data={rawData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 10,
|
||||
bottom: 60,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="timestamp" />
|
||||
<YAxis />
|
||||
<Tooltip />
|
||||
<Legend />
|
||||
<Line
|
||||
type="monotone"
|
||||
dataKey={categorie}
|
||||
stroke="#8884d8"
|
||||
activeDot={{ r: 8 }}
|
||||
/>
|
||||
<ReferenceLine y={getAvg()} label="Moy" stroke="red" />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MeteoGraph;
|
||||
117
Front-end/src/components/MeteoInfos.jsx
Normal file
@ -0,0 +1,117 @@
|
||||
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";
|
||||
|
||||
function MeteoInfos({
|
||||
object,
|
||||
defAffTempGraph,
|
||||
AffTempGraph,
|
||||
defAffPressionGraph,
|
||||
AffPressionGraph,
|
||||
defAffHumiditeGraph,
|
||||
AffHumiditeGraph,
|
||||
}) {
|
||||
const [rawData, setRawData] = useState([]);
|
||||
const identifiant = object.id;
|
||||
useEffect(() => {
|
||||
axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => {
|
||||
setRawData(response.data);
|
||||
});
|
||||
}, [object]);
|
||||
|
||||
const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null;
|
||||
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Sun className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">Météo actuelle</h1>
|
||||
</div>
|
||||
{lastData ? (
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
{lastData.temperature && (
|
||||
<div className="bg-indigo-50 rounded-lg flex items-center w-full">
|
||||
<div className="flex align-items gap-3 w-full justify-between">
|
||||
<div className="flex align-items ml-3 gap-2">
|
||||
<div className="flex items-center">
|
||||
<Thermometer className="text-red-600" size={40} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<h1 className="text-red-600 text-xl font-bold ">
|
||||
Température
|
||||
</h1>
|
||||
<h1 className="text-gray-700 text-4xl font-bold">
|
||||
{lastData.temperature} <span className="text-lg">°C</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<BoutonGraphique
|
||||
TypeAff={AffTempGraph}
|
||||
setAffichage={defAffTempGraph}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{lastData.pressure && (
|
||||
<div className="bg-indigo-50 rounded-lg flex items-center w-full">
|
||||
<div className="flex align-items gap-3 w-full justify-between">
|
||||
<div className="flex align-items ml-3 gap-2">
|
||||
<div className="flex items-center">
|
||||
<CircleGauge className="text-red-600" size={40} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<h1 className="text-red-600 text-xl font-bold ">
|
||||
Pression
|
||||
</h1>
|
||||
<h1 className="text-gray-700 text-4xl font-bold">
|
||||
{lastData.pressure} <span className="text-lg">hPa</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<BoutonGraphique
|
||||
TypeAff={AffPressionGraph}
|
||||
setAffichage={defAffPressionGraph}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{lastData.humidity && (
|
||||
<div className="bg-indigo-50 rounded-lg flex items-center w-full">
|
||||
<div className="flex align-items gap-3 w-full justify-between">
|
||||
<div className="flex align-items ml-3 gap-2">
|
||||
<div className="flex items-center">
|
||||
<Droplet className="text-red-600" size={40} />
|
||||
</div>
|
||||
<div className="flex flex-col items-start">
|
||||
<h1 className="text-red-600 text-xl font-bold ">
|
||||
Humidité
|
||||
</h1>
|
||||
<h1 className="text-gray-700 text-4xl font-bold">
|
||||
{lastData.humidity} <span className="text-lg">%</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<BoutonGraphique
|
||||
TypeAff={AffHumiditeGraph}
|
||||
setAffichage={defAffHumiditeGraph}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<h1 className="text-gray-500 text-sm">
|
||||
Dernier enregistrement : {lastData.timestamp}
|
||||
</h1>
|
||||
</div>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default MeteoInfos;
|
||||
80
Front-end/src/components/ModifObject.jsx
Normal file
@ -0,0 +1,80 @@
|
||||
import React, { use } from "react";
|
||||
import { Info } from "lucide-react";
|
||||
import { useState } from "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 || "");
|
||||
const [response, setResponse] = useState(null);
|
||||
|
||||
function makeChange(){
|
||||
axios
|
||||
.post(`${API_BASE_URL}/modifObjet`, {
|
||||
id : object.id,
|
||||
description: description,
|
||||
type: type,
|
||||
location: location,
|
||||
status: status
|
||||
})
|
||||
.then((response) => {
|
||||
setResponse(response.data[0]);
|
||||
});
|
||||
defafficherModif(false);
|
||||
window.location.reload();
|
||||
}
|
||||
function cancelChange(){
|
||||
defafficherModif(false);
|
||||
}
|
||||
function handleDescriptionChange(event){
|
||||
setDescription(event.target.value);
|
||||
}
|
||||
function handleTypeChange(event){
|
||||
setType(event.target.value);
|
||||
}
|
||||
function handleLocalisationChange(event){
|
||||
setLocalisation(event.target.value);
|
||||
}
|
||||
function handleStatusChange(event){
|
||||
setStatus(event.target.value);
|
||||
}
|
||||
return (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-9">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Info className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">Modifier les infos</h1>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Description :</p>
|
||||
<input className="text-gray-600 border" type="text" value={description} onChange={handleDescriptionChange}/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Type :</p>
|
||||
<input className="text-gray-600 border" type="text" value={type} onChange={handleTypeChange}/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Localisation :</p>
|
||||
<input className="text-gray-600 border" type="text" value={location} onChange={handleLocalisationChange}/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<p className="text-black-900 font-bold">Status :</p>
|
||||
<input className="text-gray-600 border" type="text" value={status} onChange={handleStatusChange}/>
|
||||
</div>
|
||||
<div className="flex items-center gap-4 mb-1">
|
||||
<a className="text-blue-500 hover:cursor-pointer" onClick={(()=>makeChange())}>Confirmer les modifs</a>
|
||||
<a className="text-blue-500 hover:cursor-pointer" onClick={(()=>cancelChange())}>Annuler les modifs</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default ModifObject;
|
||||
62
Front-end/src/components/WindGraph.jsx
Normal file
@ -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 (
|
||||
<div className="custom-tooltip">
|
||||
<p><strong>Date:</strong> {timestamp}</p>
|
||||
<p><strong>Vitesse du vent:</strong> {wind_speed} km/h</p>
|
||||
<p><strong>Direction du vent:</strong> {wind_direction}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null; // Si aucun point n'est survolé
|
||||
};
|
||||
return (
|
||||
<div key={object.id} className="bg-white mb-6 p-6 rounded-xl min-w-5xl" style={{width: "100%", height: "400px"}}>
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Wind className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">Historique du vent</h1>
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart
|
||||
width={500}
|
||||
height={300}
|
||||
data={rawData}
|
||||
margin={{
|
||||
top: 5,
|
||||
right: 30,
|
||||
left: 10,
|
||||
bottom: 60,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" />
|
||||
<XAxis dataKey="timestamp" />
|
||||
<YAxis />
|
||||
<Tooltip content={<CustomTooltip/>}/>
|
||||
<Legend />
|
||||
<Line type="monotone" dataKey="wind_speed" stroke="#8884d8" activeDot={{ r: 8 }} />
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WindGraph;
|
||||
78
Front-end/src/components/WindInfo.jsx
Normal file
@ -0,0 +1,78 @@
|
||||
import { Wind } from "lucide-react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import axios from "axios";
|
||||
import { API_BASE_URL } from "../config";
|
||||
|
||||
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 (
|
||||
<div key={object.id} className="bg-white p-6 rounded-xl min-w-5xl">
|
||||
<div className="flex align-items gap-6">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<Wind className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-black text-2xl font-bold mb-1 ">Vent actuel</h1>
|
||||
</div>
|
||||
{lastData ? (
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex flex-row gap-4">
|
||||
<div className="flex flex-col items-center g-2">
|
||||
<img
|
||||
src={`./src/img/${lastData.wind_direction}.png`}
|
||||
alt="Wind Direction"
|
||||
className="h-45"
|
||||
/>
|
||||
<h1 className="text-gray-600 text-xl font-bold mb-1 ">
|
||||
{lastData.wind_direction}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="flex align-items gap-1">
|
||||
<div className="flex items-center">
|
||||
<Wind className="text-red-600" size={24} />
|
||||
</div>
|
||||
<h1 className="text-red-600 text-xl font-bold ">Valeur</h1>
|
||||
</div>
|
||||
<h1 className="text-gray-700 text-4xl font-bold">
|
||||
{lastData.wind_speed} <span className="text-lg">km/h</span>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
{!AffWindGraph ? (
|
||||
<button
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 mt-4 px-4 rounded-full"
|
||||
onClick={() => defAffWindGraph(true)}
|
||||
>
|
||||
Afficher l'historique
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 mt-4 px-4 rounded-full"
|
||||
onClick={() => defAffWindGraph(false)}
|
||||
>
|
||||
Masquer l'historique
|
||||
</button>
|
||||
)}
|
||||
|
||||
<h1 className="text-gray-500 text-sm">
|
||||
Dernier enregistrement : {lastData.timestamp}
|
||||
</h1>
|
||||
</div>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WindInfo;
|
||||
1
Front-end/src/config.js
Normal file
@ -0,0 +1 @@
|
||||
export const API_BASE_URL = 'http://localhost:8888';
|
||||
BIN
Front-end/src/img/Est.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Front-end/src/img/Nord-Est.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Front-end/src/img/Nord-Ouest.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Front-end/src/img/Nord.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
Front-end/src/img/Ouest.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
Front-end/src/img/Sud-Est.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Front-end/src/img/Sud-Ouest.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
Front-end/src/img/Sud.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
@ -22,8 +22,7 @@ function Gestion() {
|
||||
Bienvenue dans le module <b>Gestion</b>.
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto">
|
||||
Dans ce module, vous allez pouvoir gerer les objets connectés de
|
||||
l'hopital.
|
||||
Ce module vous permet de gérer les objets connectés de l'hôpital de manière simple et efficace.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -34,17 +33,16 @@ function Gestion() {
|
||||
<RadioTower className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
Gestion des objets connectés
|
||||
Consulter les objets connectés météorologiques
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4">
|
||||
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.
|
||||
</p>
|
||||
<a
|
||||
href="/gestionObjets"
|
||||
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||
>
|
||||
Explorer les lieux <ArrowRight size={16} className="ml-2" />
|
||||
Explorer les objets <ArrowRight size={16} className="ml-2" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-shadow">
|
||||
@ -52,17 +50,16 @@ function Gestion() {
|
||||
<Settings className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
Configurer des services
|
||||
Ajouter un nouvel objet connecté
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4">
|
||||
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.
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||
>
|
||||
Explorer les lieux <ArrowRight size={16} className="ml-2" />
|
||||
Ajouter un objet <ArrowRight size={16} className="ml-2" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-shadow">
|
||||
@ -70,36 +67,16 @@ function Gestion() {
|
||||
<Binoculars className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
Surveillance et optimisation des ressources
|
||||
Optimisation et surveillance des ressources
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Restez informé des derniers événements, festivals et
|
||||
rassemblements communautaires dans votre région.
|
||||
Suivez les performances des dispositifs connectés et optimisez leur utilisation pour une gestion efficace des ressources.
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||
>
|
||||
Voir les événements <ArrowRight size={16} className="ml-2" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-shadow">
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<ChartArea className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">
|
||||
Rapports statistiques
|
||||
</h3>
|
||||
<p className="text-gray-600 mb-4">
|
||||
Accédez en temps réel aux horaires et aux itinéraires des bus, des
|
||||
trains et des autres transports publics.
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||
>
|
||||
Vérifier les horaires <ArrowRight size={16} className="ml-2" />
|
||||
En savoir plus <ArrowRight size={16} className="ml-2" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -23,7 +23,6 @@ function ObjectManagement() {
|
||||
useEffect(() => {
|
||||
axios.get("http://localhost:8888/objets").then((response) => {
|
||||
setObjects(response.data);
|
||||
console.log(response.data);
|
||||
});
|
||||
}, []);
|
||||
return (
|
||||
|
||||
@ -1,58 +1,96 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Search,
|
||||
MapPin,
|
||||
Calendar,
|
||||
Bus,
|
||||
ArrowRight,
|
||||
LogIn,
|
||||
UserPlus,
|
||||
RadioTower,
|
||||
Binoculars,
|
||||
Settings,
|
||||
ChartArea,
|
||||
} from "lucide-react";
|
||||
import { Thermometer, CircleGauge, Droplet } from "lucide-react";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import axios from "axios";
|
||||
import { useFetcher } from "react-router-dom";
|
||||
|
||||
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";
|
||||
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 [afficherModif, defafficherModif] = useState(false);
|
||||
const [AffWindGraph, defAffWindGraph] = useState(false);
|
||||
const [AffTempGraph, defAffTempGraph] = useState(false);
|
||||
const [AffPressionGraph, defAffPressionGraph] = useState(false);
|
||||
const [AffHumiditeGraph, defAffHumideGraph] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
axios.get(`http://localhost:8888/objet?id=${identifiant}`).then((response) => {
|
||||
axios
|
||||
.get(`http://localhost:8888/objet?id=${identifiant}`)
|
||||
.then((response) => {
|
||||
setObject(response.data[0]);
|
||||
console.log(response.data);
|
||||
});
|
||||
}, [identifiant]);
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-50">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="text-center mb-12">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-4">
|
||||
{object.name}
|
||||
<div className=" max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
||||
<div className="text-center mb-5">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-12">
|
||||
Tableau de bord - {object.name}
|
||||
</h2>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-8">
|
||||
<div
|
||||
key={object.id}
|
||||
className="bg-white p-6 rounded-xl shadow-sm hover:shadow-md transition-shadow"
|
||||
>
|
||||
<div className="w-12 h-12 bg-indigo-100 rounded-lg flex items-center justify-center mb-4">
|
||||
<RadioTower className="text-indigo-600" size={24} />
|
||||
</div>
|
||||
<h3 className="text-xl font-semibold mb-2">{object.name}</h3>
|
||||
<p className="text-gray-600 mb-4">{object.description}</p>
|
||||
<a
|
||||
href={`/objet?id=${object.id}`}
|
||||
className="flex items-center text-indigo-600 hover:text-indigo-700"
|
||||
>
|
||||
Plus d'infos <ArrowRight size={16} className="ml-2" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="grid md:grid-cols-3 gap-8 mb-5">
|
||||
{!afficherModif && (
|
||||
<InfoObjet object={object} defafficherModif={defafficherModif} />
|
||||
)}
|
||||
{afficherModif && (
|
||||
<ModifObject object={object} defafficherModif={defafficherModif} />
|
||||
)}
|
||||
|
||||
{object && object.id ? (
|
||||
<WindInfo
|
||||
object={object}
|
||||
defAffWindGraph={defAffWindGraph}
|
||||
AffWindGraph={AffWindGraph}
|
||||
/>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
)}
|
||||
{object && object.id ? (
|
||||
<MeteoInfos
|
||||
object={object}
|
||||
defAffTempGraph={defAffTempGraph}
|
||||
AffTempGraph={AffTempGraph}
|
||||
defAffPressionGraph={defAffPressionGraph}
|
||||
AffPressionGraph={AffPressionGraph}
|
||||
defAffHumiditeGraph={defAffHumideGraph}
|
||||
AffHumiditeGraph={AffHumiditeGraph}
|
||||
/>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
)}
|
||||
</div>
|
||||
{AffWindGraph &&
|
||||
(object && object.id ? (
|
||||
<WindGraph object={object} />
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
))}
|
||||
{AffTempGraph &&
|
||||
(object && object.id ? (
|
||||
<MeteoGraph object={object} categorie={"temperature"} Logo={Thermometer}/>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
))}
|
||||
{AffPressionGraph &&
|
||||
(object && object.id ? (
|
||||
<MeteoGraph object={object} categorie={"pressure"} Logo={CircleGauge}/>
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
))}
|
||||
{AffHumiditeGraph &&
|
||||
(object && object.id ? (
|
||||
<MeteoGraph object={object} categorie={"humidity"} Logo={Droplet} />
|
||||
) : (
|
||||
<p>Chargement des données...</p>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||