diff --git a/Back-end/keystore.jceks b/Back-end/keystore.jceks new file mode 100644 index 0000000..1f4ee58 Binary files /dev/null and b/Back-end/keystore.jceks differ diff --git a/Back-end/keystore.jceks.old b/Back-end/keystore.jceks.old new file mode 100644 index 0000000..f00730b Binary files /dev/null and b/Back-end/keystore.jceks.old differ diff --git a/Back-end/mvnw b/Back-end/mvnw old mode 100644 new mode 100755 diff --git a/Back-end/pom.xml b/Back-end/pom.xml index 15670a8..5d0ce82 100644 --- a/Back-end/pom.xml +++ b/Back-end/pom.xml @@ -82,6 +82,18 @@ ${junit-jupiter.version} test + + at.favre.lib + bcrypt + 0.9.0 + + + io.vertx + vertx-auth-jwt + 4.5.13 + + + diff --git a/Back-end/src/main/java/com/example/starter/AuthHandler.java b/Back-end/src/main/java/com/example/starter/AuthHandler.java new file mode 100644 index 0000000..2052b71 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/AuthHandler.java @@ -0,0 +1,113 @@ +package com.example.starter; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import at.favre.lib.crypto.bcrypt.BCrypt; +import io.vertx.ext.auth.jwt.JWTAuth; +import com.example.starter.auth.JwtAuthProvider; +import io.vertx.sqlclient.Tuple; + +public class AuthHandler { + private final DatabaseService databaseService; + private final JWTAuth jwtAuth; + + public AuthHandler(DatabaseService databaseService, JWTAuth jwtAuth) { + this.databaseService = databaseService; + this.jwtAuth = jwtAuth; + } + + public void handleSignup(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Requête invalide").encode()); + return; + } + + String name = body.getString("name"); + String surname = body.getString("surname"); + String email = body.getString("email"); + String gender = body.getString("gender"); + String password = body.getString("password"); + + if (name == null || surname == null || email == null || gender == null || password == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Tous les champs sont requis").encode()); + return; + } + + String hashedPassword = BCrypt.withDefaults().hashToString(12, password.toCharArray()); + + databaseService.pool + .preparedQuery("INSERT INTO users (name, surname, email, gender, password) VALUES (?, ?, ?, ?, ?)") + .execute(Tuple.of(name, surname, email, gender, hashedPassword)) + .onSuccess(result -> { + context.response() + .setStatusCode(201) + .end(new JsonObject().put("message", "Utilisateur inscrit avec succès").encode()); + }) + .onFailure(err -> { + System.err.println("Erreur d'inscription : " + err.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur d'inscription").encode()); + }); + } + + public void handleLogin(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Requête invalide").encode()); + return; + } + + String email = body.getString("email"); + String password = body.getString("password"); + + if (email == null || password == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Email et mot de passe requis").encode()); + return; + } + + databaseService.pool + .preparedQuery("SELECT password FROM users WHERE email = ?") + .execute(Tuple.of(email)) + .onSuccess(result -> { + if (result.rowCount() == 0) { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + return; + } + + String storedHashedPassword = result.iterator().next().getString("password"); + BCrypt.Result verification = BCrypt.verifyer().verify(password.toCharArray(), storedHashedPassword); + + if (verification.verified) { + JsonObject claims = new JsonObject().put("sub", email).put("role", "user"); + String token = jwtAuth.generateToken(claims); + context.response() + .setStatusCode(200) + .end(new JsonObject().put("token", token).encode()); + } else { + context.response() + .setStatusCode(401) + .end(new JsonObject().put("error", "Email ou mot de passe incorrect").encode()); + } + }) + .onFailure(err -> { + System.err.println("Erreur de connexion : " + err.getMessage()); + context.response() + .setStatusCode(500) + .end(new JsonObject().put("error", "Erreur serveur").encode()); + }); + } +} diff --git a/Back-end/src/main/java/com/example/starter/DatabaseService.java b/Back-end/src/main/java/com/example/starter/DatabaseService.java index 1985cc4..f4e05d5 100644 --- a/Back-end/src/main/java/com/example/starter/DatabaseService.java +++ b/Back-end/src/main/java/com/example/starter/DatabaseService.java @@ -3,6 +3,8 @@ package com.example.starter; import io.vertx.core.Vertx; import io.vertx.jdbcclient.JDBCConnectOptions; import io.vertx.jdbcclient.JDBCPool; +import io.vertx.ext.auth.jwt.JWTAuth; +import io.vertx.ext.auth.jwt.JWTAuthOptions; import io.vertx.sqlclient.PoolOptions; public class DatabaseService { @@ -11,7 +13,7 @@ public class DatabaseService { public DatabaseService(Vertx vertx) { pool = JDBCPool.pool(vertx, new JDBCConnectOptions() - .setJdbcUrl("jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=UTF-8") // URL de la base de données + .setJdbcUrl("jdbc:postgresql://localhost:5432/users?useUnicode=true&characterEncoding=UTF-8") //Url de la bdd .setUser("postgres") // Nom d'utilisateur PostgreSQL .setPassword("admin"), // Mot de passe PostgreSQL new PoolOptions() diff --git a/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java new file mode 100644 index 0000000..01d26d9 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/JwtAuthProvider.java @@ -0,0 +1,18 @@ +package com.example.starter.auth; + +import io.vertx.core.Vertx; +import io.vertx.ext.auth.jwt.JWTAuth; +import io.vertx.ext.auth.jwt.JWTAuthOptions; +import io.vertx.ext.auth.KeyStoreOptions; +import com.example.starter.auth.JwtAuthProvider; + + +public class JwtAuthProvider { + + public static JWTAuth createJwtAuth(Vertx vertx) { + return JWTAuth.create(vertx, new JWTAuthOptions() + .setKeyStore(new KeyStoreOptions() + .setPath("keystore.jceks") + .setPassword("secret"))); + } +} 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 5054e48..00a2cd1 100644 --- a/Back-end/src/main/java/com/example/starter/MainVerticle.java +++ b/Back-end/src/main/java/com/example/starter/MainVerticle.java @@ -1,48 +1,74 @@ package com.example.starter; -import io.vertx.ext.web.handler.BodyHandler; -import io.vertx.ext.web.handler.CorsHandler; -import io.vertx.core.http.HttpMethod; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; - +import io.vertx.core.http.HttpMethod; import io.vertx.ext.web.Router; +import io.vertx.ext.web.handler.BodyHandler; +import io.vertx.ext.web.handler.CorsHandler; +import io.vertx.ext.auth.jwt.JWTAuth; +import com.example.starter.auth.JwtAuthProvider; +import io.vertx.ext.web.handler.JWTAuthHandler; + public class MainVerticle extends AbstractVerticle { - private DatabaseService databaseService; + private DatabaseService databaseService; + private Router router; - @Override - public void start(Promise startPromise) throws Exception { - databaseService = new DatabaseService(vertx); - 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("*") // 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(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); + @Override + public void start(Promise startPromise) throws Exception { + databaseService = new DatabaseService(vertx); + + // Initialisation du fournisseur JWT + JWTAuth jwtAuth = JwtAuthProvider.createJwtAuth(vertx); + - vertx.createHttpServer() - .requestHandler(router) - .listen(8888) - .onSuccess(server -> { - System.out.println("HTTP server started on port " + server.actualPort()); - startPromise.complete(); - }) - .onFailure(throwable -> { - throwable.printStackTrace(); - startPromise.fail(throwable); - }); - } -} \ No newline at end of file + // Initialisation du routeur + router = Router.router(vertx); + router.route().handler(BodyHandler.create()); + router.route().handler(CorsHandler.create() + .addOrigin("*") + .allowedMethod(HttpMethod.GET) + .allowedMethod(HttpMethod.POST) + .allowedHeader("Content-Type") + .allowedHeader("Authorization")); + + // Protéger toutes les routes commençant par "/api/" + router.route("/api/*").handler(JWTAuthHandler.create(jwtAuth)); + + + // Initialisation des handlers de requêtes + QueryObjects queryObjects = new QueryObjects(databaseService); + QueryWeatherData queryWeather = new QueryWeatherData(databaseService); + SetObjects setObjects = new SetObjects(databaseService); + SetWeatherData setWeatherData = new SetWeatherData(databaseService); + AuthHandler authHandler = new AuthHandler(databaseService, jwtAuth); + + // Déclaration des routes + 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); + router.get("/getRange").handler(queryWeather::getRangeData); + router.post("/modifRangeData").handler(setWeatherData::setRangeData); + + // Routes d'authentification + router.post("/signup").handler(authHandler::handleSignup); + router.post("/login").handler(authHandler::handleLogin); + + // Création du serveur HTTP + vertx.createHttpServer() + .requestHandler(router) + .listen(8888) + .onSuccess(server -> { + System.out.println("HTTP server started on port " + server.actualPort()); + startPromise.complete(); + }) + .onFailure(throwable -> { + throwable.printStackTrace(); + startPromise.fail(throwable); + }); + } +} diff --git a/Back-end/src/main/java/com/example/starter/QueryWeatherData.java b/Back-end/src/main/java/com/example/starter/QueryWeatherData.java index 2f3ba4a..4a0dbb6 100644 --- a/Back-end/src/main/java/com/example/starter/QueryWeatherData.java +++ b/Back-end/src/main/java/com/example/starter/QueryWeatherData.java @@ -78,6 +78,36 @@ public class QueryWeatherData { .end((convertRowsToJson(rows)).encode()); }); } + public void getRangeData(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_min,temperature_max,pressure_min,pressure_max,humidity_min,humidity_max FROM range_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("error","Erreur de récupération de la BDD").encode()); + return; + }) + .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"); diff --git a/Back-end/src/main/java/com/example/starter/SetWeatherData.java b/Back-end/src/main/java/com/example/starter/SetWeatherData.java new file mode 100644 index 0000000..4c2fb56 --- /dev/null +++ b/Back-end/src/main/java/com/example/starter/SetWeatherData.java @@ -0,0 +1,65 @@ +package com.example.starter; + +import io.vertx.core.json.JsonObject; +import io.vertx.ext.web.RoutingContext; +import io.vertx.sqlclient.Tuple; + +public class SetWeatherData { + private DatabaseService databaseService; + + public SetWeatherData(DatabaseService ddbs) { + this.databaseService = ddbs; + } + + public void setRangeData(RoutingContext context) { + JsonObject body = context.body().asJsonObject(); + if (body == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Corps de la requête manquant").encode()); + return; + } + String id = body.getString("id"); + Double min = body.getDouble("min"); + Double max = body.getDouble("max"); + String type = body.getString("type"); + + if (id == null || min == null || max == null || type == null) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Paramètres manquants ou invalides").encode()); + return; + } + if (!type.matches("^[a-zA-Z_]+$")) { + context.response() + .setStatusCode(400) + .end(new JsonObject().put("error", "Type invalide").encode()); + return; + } + String query = String.format("UPDATE range_data SET %s_min=?, %s_max=? WHERE station_id=?", type, type); + + databaseService.pool + .preparedQuery( + query) + .execute(Tuple.of(min, max, 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.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", "Les limites ont bien été mis à jour").encode()); + return; + }); + } + +} diff --git a/Front-end/index.html b/Front-end/index.html index 42b0955..e0bb005 100644 --- a/Front-end/index.html +++ b/Front-end/index.html @@ -4,6 +4,7 @@ + Projet Dev Web diff --git a/Front-end/package-lock.json b/Front-end/package-lock.json index e7dd9f1..d61485a 100644 --- a/Front-end/package-lock.json +++ b/Front-end/package-lock.json @@ -9,6 +9,9 @@ "version": "0.0.0", "license": "ISC", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^7.0.1", "axios": "^1.8.4", "lucide-react": "^0.427.0", "react": "^18.3.1", @@ -61,7 +64,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", @@ -114,7 +116,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", - "dev": true, "dependencies": { "@babel/parser": "^7.26.10", "@babel/types": "^7.26.10", @@ -146,7 +147,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, "dependencies": { "@babel/traverse": "^7.25.9", "@babel/types": "^7.25.9" @@ -185,7 +185,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -194,7 +193,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "engines": { "node": ">=6.9.0" } @@ -225,7 +223,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.10.tgz", "integrity": "sha512-6aQR2zGE/QFi8JpDLjUZEPYOs7+mhKXm86VaKFiLP35JQwQb6bwUE+XbvkH0EptsYhbNBSUGaUBLKqxH1xSgsA==", - "dev": true, "dependencies": { "@babel/types": "^7.26.10" }, @@ -282,7 +279,6 @@ "version": "7.26.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/parser": "^7.26.9", @@ -296,7 +292,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.10.tgz", "integrity": "sha512-k8NuDrxr0WrPH5Aupqb2LCVURP/S0vBEn5mK6iH+GIYob66U5EtoZvcdudR2jQ4cmTwhEwW1DLB+Yyas9zjF6A==", - "dev": true, "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/generator": "^7.26.10", @@ -314,7 +309,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "engines": { "node": ">=4" } @@ -323,7 +317,6 @@ "version": "7.26.10", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", - "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.25.9", "@babel/helper-validator-identifier": "^7.25.9" @@ -332,6 +325,158 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -922,7 +1067,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -936,7 +1080,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -945,7 +1088,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -953,19 +1095,236 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.0.1.tgz", + "integrity": "sha512-T5DNVnSD9pMbj4Jk/Uphz+yvj9dfpl2+EqsOuJtG12HxEihNG5pd3qzX5yM1Id4dDwKRvM3dPVcxyzavTFhJeA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.0.1.tgz", + "integrity": "sha512-tQwjIIsn/UUSCHoCIQVkANuLua67h7Ro9M9gIHoGWaFbJFuF6cSO4Oda2olDVqIs4SWG+PaDChuu6SngxsaoyQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@mui/core-downloads-tracker": "^7.0.1", + "@mui/system": "^7.0.1", + "@mui/types": "^7.4.0", + "@mui/utils": "^7.0.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.0.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.0.1.tgz", + "integrity": "sha512-1kQ7REYjjzDukuMfTbAjm3pLEhD7gUMC2bWhg9VD6f6sHzyokKzX0XHzlr3IdzNWBjPytGkzHpPIRQrUOoPLCQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@mui/utils": "^7.0.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.0.1.tgz", + "integrity": "sha512-BeGe4xZmF7tESKhmctYrL54Kl25kGHPKVdZYM5qj5Xz76WM/poY+d8EmAqUesT6k2rbJWPp2gtOAXXinNCGunQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.0.1.tgz", + "integrity": "sha512-pK+puz0hRPHEKGlcPd80mKYD3jpyi0uVIwWffox1WZgPTQMw2dCKLcD+9ndMDJADnrKzmKlpoH756PPFh2UvWA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@mui/private-theming": "^7.0.1", + "@mui/styled-engine": "^7.0.1", + "@mui/types": "^7.4.0", + "@mui/utils": "^7.0.1", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.0.tgz", + "integrity": "sha512-TxJ4ezEeedWHBjOmLtxI203a9DII9l4k83RXmz1PYSAmnyEcK2PglTNmJGxswC/wM5cdl9ap2h8lnXvt2swAGQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.0.1.tgz", + "integrity": "sha512-SJKrrebNpmK9rJCnVL29nGPhPXQYtBZmb7Dsp0f58uIUhQfAKcBXHE4Kjs06SX4CwqeCuwEVgcHY+MgAO6XQ/g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.10", + "@mui/types": "^7.4.0", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1011,6 +1370,16 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.37.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", @@ -1393,6 +1762,12 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -1425,6 +1800,15 @@ "@types/react": "^17.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz", @@ -1754,13 +2138,27 @@ "version": "1.8.4", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1886,7 +2284,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "engines": { "node": ">=6" } @@ -2041,6 +2438,31 @@ "node": ">=18" } }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2246,7 +2668,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "dependencies": { "ms": "^2.1.3" }, @@ -2389,70 +2810,13 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "license": "MIT", "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "is-arrayish": "^0.2.1" } }, "node_modules/es-define-property": { @@ -2610,7 +2974,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -2932,6 +3295,12 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3350,6 +3719,21 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/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/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3363,7 +3747,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3405,59 +3788,11 @@ "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", "license": "ISC" }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3505,7 +3840,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "dependencies": { "hasown": "^2.0.2" }, @@ -3876,7 +4210,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -3890,6 +4223,12 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -3967,8 +4306,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -4102,8 +4440,7 @@ "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mz": { "version": "2.7.0", @@ -4354,7 +4691,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "dependencies": { "callsites": "^3.0.0" }, @@ -4362,6 +4698,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4383,8 +4737,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.11.1", @@ -4408,11 +4761,19 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -4953,7 +5314,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", @@ -4973,7 +5333,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "engines": { "node": ">=4" } @@ -5292,6 +5651,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5507,6 +5875,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -5545,7 +5919,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, diff --git a/Front-end/package.json b/Front-end/package.json index 67fd90d..ca037c9 100644 --- a/Front-end/package.json +++ b/Front-end/package.json @@ -10,6 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/material": "^7.0.1", "axios": "^1.8.4", "lucide-react": "^0.427.0", "react": "^18.3.1", diff --git a/Front-end/public/images/snow.jpg b/Front-end/public/images/snow.jpg new file mode 100644 index 0000000..a9e9abd Binary files /dev/null and b/Front-end/public/images/snow.jpg differ diff --git a/Front-end/src/App.jsx b/Front-end/src/App.jsx index ddc4e46..3059717 100644 --- a/Front-end/src/App.jsx +++ b/Front-end/src/App.jsx @@ -1,4 +1,5 @@ import { BrowserRouter as Router, Routes, Route } from "react-router-dom"; +import { AuthProvider } from "./AuthContext.jsx"; import Home from "./pages/Home.jsx"; import About from "./pages/About.jsx"; import Gestion from "./pages/Gestion/Gestion.jsx"; @@ -6,32 +7,40 @@ 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 Sidebar from './pages/Admin/sidebar.jsx'; -import User from './pages/Admin/User.jsx'; +import Signup from "./pages/Signup.jsx"; +import Login from "./pages/Login.jsx"; +import Settings from "./pages/Settings.jsx"; +import Sidebar from "./pages/Admin/sidebar.jsx"; +import User from "./pages/Admin/User.jsx"; import Dashboard from "./pages/Admin/Dashboard.jsx"; import AdminObjet from "./pages/Admin/AdminObjet.jsx"; function App() { - return ( - -
-
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - -
-
+ + {" "} + {/* Enveloppe l'application avec AuthProvider */} + +
+
+ + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + +
+
+
); } diff --git a/Front-end/src/AuthContext.jsx b/Front-end/src/AuthContext.jsx new file mode 100644 index 0000000..ca5abc9 --- /dev/null +++ b/Front-end/src/AuthContext.jsx @@ -0,0 +1,42 @@ +// src/AuthContext.js +import React, { createContext, useContext, useState, useEffect } from "react"; + +// Créer le contexte +const AuthContext = createContext(); + +// Hook pour accéder facilement au contexte +export const useAuth = () => useContext(AuthContext); + +// Fournisseur de contexte qui gère l'état du token +export const AuthProvider = ({ children }) => { + const [token, setToken] = useState(localStorage.getItem("token")); + + // Met à jour le token lorsque localStorage change + useEffect(() => { + const handleStorageChange = () => { + setToken(localStorage.getItem("token")); + }; + + window.addEventListener("storage", handleStorageChange); + + return () => { + window.removeEventListener("storage", handleStorageChange); + }; + }, []); + + const login = (newToken) => { + localStorage.setItem("token", newToken); + setToken(newToken); + }; + + const logout = () => { + localStorage.removeItem("token"); + setToken(null); + }; + + return ( + + {children} + + ); +}; diff --git a/Front-end/src/components/BoutonGraphique.jsx b/Front-end/src/components/BoutonGraphique.jsx index 932725f..04c7599 100644 --- a/Front-end/src/components/BoutonGraphique.jsx +++ b/Front-end/src/components/BoutonGraphique.jsx @@ -1,23 +1,21 @@ -import React, {useRef} from "react"; +import React 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" }); - } + +function BoutonGraphique({ type, setGraphStates, graphStates, graphCible }) { + const handleClick = () => { + setGraphStates((prev) => ({ ...prev, [type]: !prev[type] })); }; - return !TypeAff ? ( + return !graphStates[type] ? ( ) : ( diff --git a/Front-end/src/components/Header.jsx b/Front-end/src/components/Header.jsx index ca0873d..9bbd666 100644 --- a/Front-end/src/components/Header.jsx +++ b/Front-end/src/components/Header.jsx @@ -1,70 +1,61 @@ -import React, { useState } from "react"; -import { LogIn, UserPlus, Menu, X } from "lucide-react"; +import React, { useState, useEffect } from "react"; +import { LogIn, UserPlus, LogOut, Settings } from "lucide-react"; +import { Link } from "react-router-dom"; +import { useAuth } from "../AuthContext"; function Header() { - const [isMenuOpen, setIsMenuOpen] = useState(false); + const { token, logout } = useAuth(); return (
-
-

VigiMétéo

- - - - - -
- - +
+
+

VigiMétéo

+ +
+ {token ? ( + <> + + + + + + + ) : ( + <> + + + Connexion + + + + Inscription + + + )} +
); } -export default Header; \ No newline at end of file +export default Header; diff --git a/Front-end/src/components/InfoObject.jsx b/Front-end/src/components/InfoObject.jsx index 9c16424..b838a53 100644 --- a/Front-end/src/components/InfoObject.jsx +++ b/Front-end/src/components/InfoObject.jsx @@ -3,8 +3,8 @@ import { Info } from "lucide-react"; function InfoObject({ object,defafficherModif }) { return ( -
-
+
+
diff --git a/Front-end/src/components/MeteoGraph.jsx b/Front-end/src/components/MeteoGraph.jsx index ee957f4..0305881 100644 --- a/Front-end/src/components/MeteoGraph.jsx +++ b/Front-end/src/components/MeteoGraph.jsx @@ -15,7 +15,7 @@ import { Wind } from "lucide-react"; import axios from "axios"; import { API_BASE_URL } from "../config"; -function MeteoGraph({ object, categorie, Logo }) { +function MeteoGraph({ object, categorie, Logo,reference}) { const [rawData, setRawData] = useState([]); const identifiant = object.id; useEffect(() => { @@ -23,7 +23,11 @@ function MeteoGraph({ object, categorie, Logo }) { setRawData(response.data); }); }, [object]); - + useEffect(() => { + if (reference?.current) { + reference.current.scrollIntoView({ behavior: "smooth" }); + } + }, [reference]); function getAvg() { let moyenne = 0; rawData.forEach((element) => { @@ -35,9 +39,11 @@ function MeteoGraph({ object, categorie, Logo }) { } return (
diff --git a/Front-end/src/components/MeteoInfos.jsx b/Front-end/src/components/MeteoInfos.jsx index 1e2604b..00a4340 100644 --- a/Front-end/src/components/MeteoInfos.jsx +++ b/Front-end/src/components/MeteoInfos.jsx @@ -1,39 +1,30 @@ 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"; +import ParticularMeteo from "./ParticularMeteo"; -function MeteoInfos({ - object, - defAffTempGraph, - AffTempGraph, - defAffPressionGraph, - AffPressionGraph, - defAffHumiditeGraph, - AffHumiditeGraph, - graphCible -}) { +function MeteoInfos({ object, graphStates, setGraphStates, graphRefs }) { const [rawData, setRawData] = useState([]); - const [AffAlert,setAffAlert] = useState(false); + const [AffAlert, setAffAlert] = useState(false); + const [AffRegles, setAffRegles] = useState(false); const identifiant = object.id; useEffect(() => { axios.get(`${API_BASE_URL}/meteo?id=${identifiant}`).then((response) => { setRawData(response.data); - if(rawData.length <5){ + 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")) && ( + {AffAlert && object.status === "active" && ( )}
@@ -44,78 +35,38 @@ function MeteoInfos({
{lastData ? (
- {lastData.temperature && ( -
-
-
-
- -
-
-

- Température -

-

- {lastData.temperature} °C -

-
-
- + + - /> -
-
- )} - {lastData.pressure && ( -
-
-
-
- -
-
-

- Pression -

-

- {lastData.pressure} hPa -

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

- Humidité -

-

- {lastData.humidity} % -

-
-
- -
-
- )} +

Dernier enregistrement : {lastData.timestamp}

diff --git a/Front-end/src/components/ParticularMeteo.jsx b/Front-end/src/components/ParticularMeteo.jsx new file mode 100644 index 0000000..933774e --- /dev/null +++ b/Front-end/src/components/ParticularMeteo.jsx @@ -0,0 +1,164 @@ +import React, { useEffect, useState } from "react"; +import BoutonGraphique from "./BoutonGraphique"; +import { Bell } from "lucide-react"; +import Slider from "@mui/material/Slider"; +import { API_BASE_URL } from "../config"; +import axios from "axios"; + +const identifiant = new URLSearchParams(window.location.search).get("id"); +function ParticularMeteo({ + type, + data, + Icon, + texte1, + texte2, + graphStates, + setGraphStates, + graphRefs, +}) { + const [affRegles, setAffRegles] = useState(false); + const [rangeValue, setRangeValue] = useState([0, 0]); + const [alerteActive, setAlerteActive] = useState(false); + const minMaxValues = { + temperature: [-50, 60], + pressure: [940, 1060], + humidity: [10, 100], + }; + const MIN = minMaxValues[type][0]; + const MAX = minMaxValues[type][1]; + const formatLabel = (value) => { + switch (type) { + case "temperature": + return `${value}°C`; + case "pressure": + return `${value} hPa`; + case "humidity": + return `${value}%`; + default: + return value; + } + }; + const marks = [ + { + value: MIN, + label: formatLabel(MIN), + }, + { + value: MAX, + label: formatLabel(MAX), + }, + ]; + useEffect(() => { + axios.get(`${API_BASE_URL}/getRange?id=${identifiant}`).then((response) => { + setRangeValue([ + response.data[0][type + "_min"], + response.data[0][type + "_max"], + ]); + }); + }, [identifiant]); + const color = + rangeValue[0] > data[type] || rangeValue[1] < data[type] + ? "text-red-600" + : "text-indigo-600"; + + const defRangeData = () => { + console.log("Données envoyées :", { + id: identifiant, + min: rangeValue[0], + max: rangeValue[1], + type, + }); + axios + .post(`${API_BASE_URL}/modifRangeData`, { + id: identifiant, + min: parseFloat(rangeValue[0]), + max: parseFloat(rangeValue[1]), + type, + }) + .then((response) => { + console.log("Modification réussie :", response.data); + }) + .catch((error) => { + console.error("Erreur lors de la modification :", error); + }); + window.location.reload(); + }; + + const handleChange = (event, newValue) => { + setRangeValue(newValue); + }; + function valuetext(value) { + return `${value}°C`; + } + if (data[type]) { + return ( +
+
+
+
+ +
+
+

{texte1}

+

+ {Math.round(data[type])}{" "} + {texte2} +

+
+
+
+ + + +
+
+ {affRegles && ( +
+

+ Définissez la valeur seuil pour l'alerte : +

+
+ "Temperature range"} + value={rangeValue} + onChange={handleChange} + valueLabelDisplay="auto" + min={MIN} + max={MAX} + marks={marks} + getAriaValueText={valuetext} + disableSwap + /> +
+ {color=="text-red-600" &&( +

Attention, la valeur actuelle est hors des bornes que vous avez définit !

+ )} + +
+ )} +
+ ); + } +} + +export default ParticularMeteo; diff --git a/Front-end/src/components/WindGraph.jsx b/Front-end/src/components/WindGraph.jsx index 575d8da..1509c96 100644 --- a/Front-end/src/components/WindGraph.jsx +++ b/Front-end/src/components/WindGraph.jsx @@ -5,7 +5,7 @@ import { Wind } from "lucide-react"; import axios from "axios"; import { API_BASE_URL } from "../config"; -function WindGraph({ object }) { +function WindGraph({ object,reference }) { const [rawData, setRawData] = useState([]); const identifiant = object.id; useEffect(() => { @@ -25,10 +25,15 @@ function WindGraph({ object }) { ); } - return null; // Si aucun point n'est survolé + return null; }; + useEffect(() => { + if (reference?.current) { + reference.current.scrollIntoView({ behavior: "smooth" }); + } + }, [reference]); return ( -
+
diff --git a/Front-end/src/components/WindInfo.jsx b/Front-end/src/components/WindInfo.jsx index ac479d5..8d94648 100644 --- a/Front-end/src/components/WindInfo.jsx +++ b/Front-end/src/components/WindInfo.jsx @@ -5,7 +5,7 @@ import axios from "axios"; import { API_BASE_URL } from "../config"; import BoutonGraphique from "./BoutonGraphique"; -function WindInfo({ object, defAffWindGraph, AffWindGraph }) { +function WindInfo({ object, setGraphStates, graphStates, graphRefs, reference}) { const [rawData, setRawData] = useState([]); const identifiant = object.id; useEffect(() => { @@ -13,6 +13,11 @@ function WindInfo({ object, defAffWindGraph, AffWindGraph }) { setRawData(response.data); }); }, [object]); + useEffect(() => { + if (reference?.current) { + reference.current.scrollIntoView({ behavior: "smooth" }); + } + }, [reference]); const lastData = rawData.length > 0 ? rawData[rawData.length - 1] : null; @@ -47,9 +52,11 @@ function WindInfo({ object, defAffWindGraph, AffWindGraph }) {
-
diff --git a/Front-end/src/img/cloud-alert.svg b/Front-end/src/img/cloud-alert.svg new file mode 100644 index 0000000..a7ea487 --- /dev/null +++ b/Front-end/src/img/cloud-alert.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Front-end/src/pages/AdminDashboard.jsx b/Front-end/src/pages/AdminDashboard.jsx new file mode 100644 index 0000000..403da86 --- /dev/null +++ b/Front-end/src/pages/AdminDashboard.jsx @@ -0,0 +1,44 @@ +import React, { useState } from "react"; +import { Calendar, Settings, LayoutDashboard } from "lucide-react"; + +function AdminDashboard() { + + const [activeTab, setActiveTab] = useState('dashboard'); + + return ( +
+ {/* SideMenu */} +
+

+ Administration +

+ +

Test

+
+
+ ); +} + +export default AdminDashboard; diff --git a/Front-end/src/pages/Gestion/Objet.jsx b/Front-end/src/pages/Gestion/Objet.jsx index c076b81..cd40f07 100644 --- a/Front-end/src/pages/Gestion/Objet.jsx +++ b/Front-end/src/pages/Gestion/Objet.jsx @@ -1,7 +1,7 @@ import React from "react"; import { Thermometer, CircleGauge, Droplet } from "lucide-react"; -import { useEffect, useState, useRef} from "react"; +import { useEffect, useState, useRef } from "react"; import axios from "axios"; import { API_BASE_URL } from "../../config"; @@ -14,31 +14,27 @@ 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, - }) + 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); + const graphRefs = { + temperature: useRef(null), + pressure: useRef(null), + humidity: useRef(null), + wind: useRef(null), + }; useEffect(() => { axios.get(`${API_BASE_URL}/objet?id=${identifiant}`).then((response) => { setObject(response.data[0]); }); }, [identifiant]); - return ( + return object && object.id ? (
@@ -52,68 +48,50 @@ function Objet() { ) : ( )} - - {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...

- ))} + + {graphStates.wind && } + {graphStates.temperature && ( + + )} + {graphStates.pressure && ( + + )} + {graphStates.humidity && ( + + )}
+ ) : ( +

Erreur de récupération de l'objet

); } export default Objet; diff --git a/Front-end/src/pages/Home.jsx b/Front-end/src/pages/Home.jsx index cb63aa6..885b92b 100644 --- a/Front-end/src/pages/Home.jsx +++ b/Front-end/src/pages/Home.jsx @@ -1,115 +1,123 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Search, MapPin, Calendar, Bus, ArrowRight, LogIn, UserPlus } from 'lucide-react'; -import { useEffect, useState } from 'react'; -import axios from 'axios'; +import { useAuth } from "../AuthContext"; + function Home() { - const [searchQuery, setSearchQuery] = useState(''); - const [activeFilter, setActiveFilter] = useState('all'); - const [name, setName] = useState([]); - return ( -
-
-
-

- Bienvenue dans ta ville intelligente. -

-

- Découvrez tout ce que votre ville a à offrir - des événements locaux aux horaires de transport, le tout en un seul endroit. + const [searchQuery, setSearchQuery] = useState(''); + const [activeFilter, setActiveFilter] = useState('all'); + const [name, setName] = useState([]); + const { token, logout } = useAuth(); + + return ( +

+
+
+

+ Bienvenue dans ta ville intelligente.

+ {token ? ( + <>

Tu es connecté

+ + ):( +

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 +
- -
-
- - setSearchQuery(e.target.value)} - /> -
-
- - - - + +
+
+
+

Evenements locaux

+

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

+ + Voir les événements +
- - {/* 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 - + +
+
+
+

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 +export default Home; + \ No newline at end of file diff --git a/Front-end/src/pages/Login.jsx b/Front-end/src/pages/Login.jsx new file mode 100644 index 0000000..5aab8e4 --- /dev/null +++ b/Front-end/src/pages/Login.jsx @@ -0,0 +1,121 @@ +import React, { useState } from 'react'; +import { Mail, Lock } from 'lucide-react'; +import { useNavigate, Link } from 'react-router-dom'; +import axios from 'axios'; // Assurez-vous d'avoir axios importé +import { useAuth } from '../AuthContext'; + +function Login() { + const [formData, setFormData] = useState({ + email: '', + password: '' + }); + const { login } = useAuth(); // Utilisation du hook useAuth pour accéder à la fonction login + const navigate = useNavigate(); // Initialisation de useNavigate + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + try { + const response = await fetch("http://localhost:8888/login", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + // Récupérer les données JSON de la réponse + const data = await response.json(); + + // Vérifiez que la réponse contient bien un token + if (data.token) { + // Appel de la fonction login du contexte pour stocker le token + login(data.token); + + // Rediriger vers la page d'accueil après la connexion + navigate('/'); + } else { + console.error('Token manquant dans la réponse'); + } + } catch (error) { + console.error('Erreur lors de la connexion', error); + } + }; + return ( +
+
+

Connexion

+
+ {/* Email */} +
+ +
+
+ +
+ +
+
+ + {/* Mot de passe */} +
+ +
+
+ +
+ +
+
+ + {/* Bouton de connexion */} +
+ +
+ + {/* Lien vers la page d'inscription */} +
+

+ Vous n'avez pas de compte ? + Inscrivez-vous ici +

+
+
+
+
+ ); +} + +export default Login; diff --git a/Front-end/src/pages/Settings.jsx b/Front-end/src/pages/Settings.jsx new file mode 100644 index 0000000..b749a20 --- /dev/null +++ b/Front-end/src/pages/Settings.jsx @@ -0,0 +1,161 @@ +import React, { useState } from 'react'; +import { Mail, User, Lock } from 'lucide-react'; +import { useNavigate, Link} from 'react-router-dom'; // Importation du hook useNavigate + +function Settings() { + const [formData, setFormData] = useState({ + name: '', + surname: '', + email: '', + gender: '', + password: '', + confirmPassword: '' + }); + const navigate = useNavigate(); // Initialisation de useNavigate + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (formData.password !== formData.confirmPassword) { + alert("Les mots de passe ne correspondent pas !"); + return; + } + + try { + const response = await fetch("http://localhost:8888/settings", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error || "Erreur lors de la modification"); + } + + alert("Modification réussie !"); + + // Redirection vers la page d'accueil après une inscription réussie + navigate("/home"); // Remplace "/home" par l'URL de ta page d'accueil + } catch (error) { + alert(error.message); + } + }; + + return ( +
+
+

Settings

+
+ {/* (Formulaire changement Email, Mot de passe) */} + + + + {/* Email */} +
+ +
+
+ +
+ +
+
+ + {/* Mot de passe */} +
+ +
+
+ +
+ +
+
+ + {/* nouveau mot de passe */} +
+ +
+
+ +
+ +
+
+
+ +
+
+ +
+ +
+
+ {/* Bouton d'inscription */} +
+ +
+
+ +
+
+ ); +} + +export default Settings; diff --git a/Front-end/src/pages/Signup.jsx b/Front-end/src/pages/Signup.jsx new file mode 100644 index 0000000..595e7ec --- /dev/null +++ b/Front-end/src/pages/Signup.jsx @@ -0,0 +1,217 @@ +import React, { useState } from 'react'; +import { Mail, User, Lock } from 'lucide-react'; +import { useNavigate, Link} from 'react-router-dom'; // Importation du hook useNavigate + +function Signup() { + const [formData, setFormData] = useState({ + name: '', + surname: '', + email: '', + gender: '', + password: '', + confirmPassword: '' + }); + const navigate = useNavigate(); // Initialisation de useNavigate + + const handleChange = (e) => { + const { name, value } = e.target; + setFormData(prev => ({ + ...prev, + [name]: value + })); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (formData.password !== formData.confirmPassword) { + alert("Les mots de passe ne correspondent pas !"); + return; + } + + try { + const response = await fetch("http://localhost:8888/signup", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(formData), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error || "Erreur lors de l'inscription"); + } + + alert("Inscription réussie !"); + + // Redirection vers la page d'accueil après une inscription réussie + navigate("/home"); // Remplace "/home" par l'URL de ta page d'accueil + } catch (error) { + alert(error.message); + } + }; + + return ( +
+
+

Inscription

+
+ {/* Formulaire (Nom, Prénom, Sexe, Email, Mot de passe) */} +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ + {/* Sexe */} +
+ +
+ + +
+
+ + {/* Email */} +
+ +
+
+ +
+ +
+
+ + {/* Mot de passe */} +
+ +
+
+ +
+ +
+
+ + {/* Confirmer mot de passe */} +
+ +
+
+ +
+ +
+
+ + {/* Bouton d'inscription */} +
+ +
+ + {/*Si il a déjà un compte*/} +
+

+ Vous avez déjà un compte ? + Connectez-vous ici +

+
+
+ +
+
+ ); +} + +export default Signup; diff --git a/sql/export.sql b/sql/export.sql new file mode 100644 index 0000000..2181aab --- /dev/null +++ b/sql/export.sql @@ -0,0 +1,318 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 17.4 +-- Dumped by pg_dump version 17.4 + +-- Started on 2025-04-08 10:16:23 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 222 (class 1259 OID 32768) +-- Name: range_data; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.range_data ( + station_id integer NOT NULL, + temperature_min numeric, + temperature_max numeric, + pressure_min numeric, + pressure_max numeric, + humidity_min numeric, + humidity_max numeric +); + + +ALTER TABLE public.range_data OWNER TO postgres; + +-- +-- TOC entry 221 (class 1259 OID 16479) +-- Name: weather_data; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.weather_data ( + id integer NOT NULL, + station_id integer NOT NULL, + temperature numeric(5,2), + humidity numeric(5,2), + pressure numeric(7,2), + wind_speed numeric(5,2), + wind_direction character varying(50), + "timestamp" timestamp without time zone DEFAULT CURRENT_TIMESTAMP +); + + +ALTER TABLE public.weather_data OWNER TO postgres; + +-- +-- TOC entry 220 (class 1259 OID 16478) +-- Name: weather_data_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.weather_data_id_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.weather_data_id_seq OWNER TO postgres; + +-- +-- TOC entry 4921 (class 0 OID 0) +-- Dependencies: 220 +-- Name: weather_data_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.weather_data_id_seq OWNED BY public.weather_data.id; + + +-- +-- TOC entry 219 (class 1259 OID 16468) +-- Name: weather_objects; Type: TABLE; Schema: public; Owner: postgres +-- + +CREATE TABLE public.weather_objects ( + id integer NOT NULL, + name character varying(500) NOT NULL, + description text, + type character varying(100) NOT NULL, + location character varying(255), + last_update timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + status character varying(50) DEFAULT 'active'::character varying, + batterie integer, + type_batterie character varying(50), + CONSTRAINT weather_objects_batterie_check CHECK (((batterie >= 0) AND (batterie <= 100))) +); + + +ALTER TABLE public.weather_objects OWNER TO postgres; + +-- +-- TOC entry 217 (class 1259 OID 16466) +-- Name: weather_objects_id_seq; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.weather_objects_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.weather_objects_id_seq OWNER TO postgres; + +-- +-- TOC entry 218 (class 1259 OID 16467) +-- Name: weather_objects_id_seq1; Type: SEQUENCE; Schema: public; Owner: postgres +-- + +CREATE SEQUENCE public.weather_objects_id_seq1 + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE public.weather_objects_id_seq1 OWNER TO postgres; + +-- +-- TOC entry 4922 (class 0 OID 0) +-- Dependencies: 218 +-- Name: weather_objects_id_seq1; Type: SEQUENCE OWNED BY; Schema: public; Owner: postgres +-- + +ALTER SEQUENCE public.weather_objects_id_seq1 OWNED BY public.weather_objects.id; + + +-- +-- TOC entry 4755 (class 2604 OID 16482) +-- Name: weather_data id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.weather_data ALTER COLUMN id SET DEFAULT nextval('public.weather_data_id_seq'::regclass); + + +-- +-- TOC entry 4752 (class 2604 OID 16471) +-- Name: weather_objects id; Type: DEFAULT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.weather_objects ALTER COLUMN id SET DEFAULT nextval('public.weather_objects_id_seq1'::regclass); + + +-- +-- TOC entry 4915 (class 0 OID 32768) +-- Dependencies: 222 +-- Data for Name: range_data; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.range_data (station_id, temperature_min, temperature_max, pressure_min, pressure_max, humidity_min, humidity_max) FROM stdin; +1 -33 42 980 1040 30 84 +2 -15 50 980 1040 30 90 +3 -15 50 980 1040 30 90 +4 -15 50 980 1040 30 90 +5 -15 50 980 1040 30 90 +6 -15 50 980 1040 30 90 +7 -15 50 980 1040 30 90 +8 -15 50 980 1040 30 90 +9 -15 50 980 1040 30 90 +10 -15 50 980 1040 30 90 +\. + + +-- +-- TOC entry 4914 (class 0 OID 16479) +-- Dependencies: 221 +-- Data for Name: weather_data; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.weather_data (id, station_id, temperature, humidity, pressure, wind_speed, wind_direction, "timestamp") FROM stdin; +1 1 21.50 60.20 1013.10 5.40 Nord-Ouest 2025-03-29 18:47:46.685241 +2 2 22.30 55.00 1012.50 3.20 Sud-Ouest 2025-03-29 18:47:46.685241 +3 3 24.10 50.00 1010.80 6.00 Est 2025-03-29 18:47:46.685241 +4 1 19.80 65.40 1014.00 4.50 Ouest 2025-03-29 18:47:46.685241 +5 2 20.60 59.30 1013.50 2.80 Nord 2025-03-29 18:47:46.685241 +6 1 22.50 60.00 1012.50 12.30 Nord-Ouest 2025-03-29 08:00:00 +7 1 23.00 65.00 1013.25 14.00 Ouest 2025-03-29 09:00:00 +8 1 24.00 70.00 1014.75 15.20 Nord 2025-03-29 10:00:00 +9 2 21.50 55.00 1011.30 11.00 Sud 2025-03-29 08:30:00 +10 2 22.00 60.00 1012.80 13.00 Est 2025-03-29 09:30:00 +11 2 23.50 63.00 1013.50 14.50 Sud-Est 2025-03-29 10:30:00 +12 3 26.00 58.00 1012.90 17.00 Ouest 2025-03-29 11:00:00 +13 3 27.00 60.00 1014.00 18.50 Nord-Ouest 2025-03-29 12:00:00 +14 3 28.00 62.00 1015.10 16.00 Nord 2025-03-29 13:00:00 +15 4 19.50 75.00 1010.00 9.50 Sud-Ouest 2025-03-29 08:00:00 +16 4 20.00 80.00 1010.50 10.00 Sud 2025-03-29 09:00:00 +17 4 21.50 85.00 1011.00 11.50 Est 2025-03-29 10:00:00 +18 5 18.00 90.00 1010.70 8.00 Ouest 2025-03-29 08:30:00 +19 5 18.50 92.00 1011.20 7.00 Nord-Ouest 2025-03-29 09:30:00 +20 5 19.00 95.00 1011.80 6.50 Nord 2025-03-29 10:30:00 +21 6 24.50 65.00 1013.90 13.00 Sud 2025-03-29 11:00:00 +22 6 25.00 66.00 1014.20 14.50 Ouest 2025-03-29 12:00:00 +23 6 26.50 68.00 1015.50 16.00 Sud-Ouest 2025-03-29 13:00:00 +24 7 21.00 60.00 1012.50 11.50 Est 2025-03-29 08:00:00 +25 7 22.50 62.00 1013.00 12.00 Nord-Ouest 2025-03-29 09:00:00 +26 7 23.00 64.00 1013.75 13.50 Sud-Est 2025-03-29 10:00:00 +27 8 25.00 58.00 1012.10 16.50 Nord 2025-03-29 08:30:00 +28 8 26.00 60.00 1013.30 17.50 Ouest 2025-03-29 09:30:00 +29 8 27.00 62.00 1014.50 18.00 Sud-Ouest 2025-03-29 10:30:00 +30 9 22.00 67.00 1011.40 14.00 Est 2025-03-29 11:00:00 +31 9 23.00 69.00 1012.60 15.00 Nord-Ouest 2025-03-29 12:00:00 +32 9 24.00 72.00 1013.80 16.00 Nord 2025-03-29 13:00:00 +33 10 18.00 55.00 1010.20 10.00 Ouest 2025-03-29 08:00:00 +34 10 19.00 58.00 1011.00 11.50 Sud-Ouest 2025-03-29 09:00:00 +35 10 20.00 60.00 1011.70 12.50 Est 2025-03-29 10:00:00 +\. + + +-- +-- TOC entry 4912 (class 0 OID 16468) +-- Dependencies: 219 +-- Data for Name: weather_objects; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +COPY public.weather_objects (id, name, description, type, location, last_update, status, batterie, type_batterie) FROM stdin; +1 Station Paris Station météo située à Paris, France. Mesures de température, humidité, pression et vent. station Paris, France 2025-04-07 20:47:06.264847 active 100 solaire +9 Station Nice Station météo située à Nice, France. Elle mesure la température, l'humidité et la pression. station Nice, France 2025-03-31 18:31:23.038047 active 100 solaire +2 Station Lyon Station météo située à Lyon, France. Mesures de température, humidité, pression et vent. station Lyon, France 2025-03-30 12:16:00.835834 active 100 solaire +3 Station Marseille Station météo située à Marseille, France. Mesures de température, humidité, pression et vent. station Marseille, France 2025-03-30 17:01:10.631653 inactive 100 solaire +4 Capteur Bordeaux Capteur de température et d'humidité à Bordeaux. capteur Bordeaux, France 2025-03-30 17:53:01.42853 active 100 solaire +5 Capteur Lille Capteur de pression atmosphérique à Lille. capteur Lille, France 2025-03-31 21:32:04.955306 inactive 100 solaire +6 Capteur Nantes Capteur de vent à Nantes. capteur Nantes, France 2025-03-30 20:10:18.547523 active 100 solaire +7 Station Toulouse Station météo à Toulouse mesurant la température, l'humidité, la pression et la vitesse du vent. station Toulouse, France 2025-04-02 15:43:34.803703 active 100 solaire +10 Capteur Paris Sud Capteur de température et humidité à Paris Sud. capteur Paris, France 2025-04-02 23:09:38.725522 inactive 100 solaire +8 Capteur Grenoble Capteur de température à Grenoble. capteur Grenoble, France 2025-04-04 10:40:08.247433 active 100 solaire +\. + + +-- +-- TOC entry 4923 (class 0 OID 0) +-- Dependencies: 220 +-- Name: weather_data_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.weather_data_id_seq', 35, true); + + +-- +-- TOC entry 4924 (class 0 OID 0) +-- Dependencies: 217 +-- Name: weather_objects_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.weather_objects_id_seq', 1, false); + + +-- +-- TOC entry 4925 (class 0 OID 0) +-- Dependencies: 218 +-- Name: weather_objects_id_seq1; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.weather_objects_id_seq1', 17, true); + + +-- +-- TOC entry 4763 (class 2606 OID 32774) +-- Name: range_data station_meteo_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.range_data + ADD CONSTRAINT station_meteo_pkey PRIMARY KEY (station_id); + + +-- +-- TOC entry 4761 (class 2606 OID 16485) +-- Name: weather_data weather_data_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.weather_data + ADD CONSTRAINT weather_data_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 4759 (class 2606 OID 16477) +-- Name: weather_objects weather_objects_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.weather_objects + ADD CONSTRAINT weather_objects_pkey PRIMARY KEY (id); + + +-- +-- TOC entry 4764 (class 2606 OID 16486) +-- Name: weather_data weather_data_station_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres +-- + +ALTER TABLE ONLY public.weather_data + ADD CONSTRAINT weather_data_station_id_fkey FOREIGN KEY (station_id) REFERENCES public.weather_objects(id); + + +-- Completed on 2025-04-08 10:16:23 + +-- +-- PostgreSQL database dump complete +-- +