diff --git a/backend/src/main/java/net/miarma/contaminus/common/Constants.java b/backend/src/main/java/net/miarma/contaminus/common/Constants.java
index 48e5eb1..706f3ab 100644
--- a/backend/src/main/java/net/miarma/contaminus/common/Constants.java
+++ b/backend/src/main/java/net/miarma/contaminus/common/Constants.java
@@ -29,7 +29,9 @@ public class Constants {
public static final String PUT_DEVICE_BY_ID = API_PREFIX + "/devices/:deviceId";
public static final String GET_DEVICE_ACTUATORS = API_PREFIX + "/devices/:deviceId/actuators";
public static final String GET_DEVICE_LATEST_VALUES = API_PREFIX + "/devices/:deviceId/latest";
-
+ public static final String GET_DEVICE_POLLUTION_MAP = API_PREFIX + "/devices/:deviceId/pollution-map";
+ public static final String GET_DEVICE_HISTORY = API_PREFIX + "/devices/:deviceId/history";
+
public static final String GET_SENSORS = API_PREFIX + "/sensors";
public static final String GET_SENSOR_BY_ID = API_PREFIX + "/sensors/:sensorId";
public static final String GET_SENSOR_VALUES = API_PREFIX + "/sensors/:sensorId/values";
@@ -40,14 +42,7 @@ public class Constants {
public static final String GET_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
public static final String POST_ACTUATORS = API_PREFIX + "/actuators";
public static final String PUT_ACTUATOR_BY_ID = API_PREFIX + "/actuators/:actuatorId";
-
- public static final String GET_GPS_VALUES = API_PREFIX + "/gps-values";
- public static final String GET_GPS_VALUE_BY_ID = API_PREFIX + "/gps-values/:valueId";
- public static final String POST_GPS_VALUES = API_PREFIX + "/gps-values";
-
- public static final String GET_AIR_VALUES = API_PREFIX + "/air-values";
- public static final String GET_AIR_VALUE_BY_ID = API_PREFIX + "/air-values/:valueId";
- public static final String POST_AIR_VALUES = API_PREFIX + "/air-values";
+
private Constants() {
throw new AssertionError("Utility class cannot be instantiated.");
diff --git a/backend/src/main/java/net/miarma/contaminus/server/ApiVerticle.java b/backend/src/main/java/net/miarma/contaminus/server/ApiVerticle.java
index f750f89..1a7b1a8 100644
--- a/backend/src/main/java/net/miarma/contaminus/server/ApiVerticle.java
+++ b/backend/src/main/java/net/miarma/contaminus/server/ApiVerticle.java
@@ -50,6 +50,8 @@ public class ApiVerticle extends AbstractVerticle {
router.route(HttpMethod.GET, Constants.GET_DEVICE_LATEST_VALUES).handler(this::getDeviceLatestValuesHandler);
router.route(HttpMethod.POST, Constants.POST_DEVICES).handler(this::postDeviceHandler);
router.route(HttpMethod.PUT, Constants.PUT_DEVICE_BY_ID).handler(this::putDeviceByIdHandler);
+ router.route(HttpMethod.GET, Constants.GET_DEVICE_POLLUTION_MAP).handler(this::getPollutionMapHandler);
+ router.route(HttpMethod.GET, Constants.GET_DEVICE_HISTORY).handler(this::getDeviceHistoryHandler);
// Sensor Routes
router.route(HttpMethod.GET, Constants.GET_SENSORS).handler(this::getSensorsHandler);
@@ -245,6 +247,26 @@ public class ApiVerticle extends AbstractVerticle {
sendQuery(query, context);
}
+
+ private void getPollutionMapHandler(RoutingContext context) {
+ String deviceId = context.request().getParam("deviceId");
+ String query = QueryBuilder
+ .select("*")
+ .from("v_pollution_map")
+ .where("deviceId = ?", deviceId)
+ .build();
+ sendQuery(query, context);
+ }
+
+ private void getDeviceHistoryHandler(RoutingContext context) {
+ String deviceId = context.request().getParam("deviceId");
+ String query = QueryBuilder
+ .select("*")
+ .from("v_sensor_history_by_device")
+ .where("deviceId = ?", deviceId)
+ .build();
+ sendQuery(query, context);
+ }
// Sensor Handlers
private void getSensorsHandler(RoutingContext context) {
diff --git a/backend/src/main/java/net/miarma/contaminus/server/MainVerticle.java b/backend/src/main/java/net/miarma/contaminus/server/MainVerticle.java
index e57eb7a..a351c67 100644
--- a/backend/src/main/java/net/miarma/contaminus/server/MainVerticle.java
+++ b/backend/src/main/java/net/miarma/contaminus/server/MainVerticle.java
@@ -14,7 +14,7 @@ public class MainVerticle extends AbstractVerticle {
getVertx().deployVerticle(new DatabaseVerticle(), options);
getVertx().deployVerticle(new ApiVerticle(), options);
- getVertx().deployVerticle(new HttpServerVerticle());
+ //getVertx().deployVerticle(new HttpServerVerticle());
}
@Override
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 3ee2330..1ad79c1 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -19,7 +19,8 @@
"react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0",
- "react-leaflet": "^5.0.0"
+ "react-leaflet": "^5.0.0",
+ "react-router-dom": "^7.3.0"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
@@ -1424,6 +1425,12 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/cookie": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
+ "license": "MIT"
+ },
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
@@ -1917,6 +1924,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cookie": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
+ "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -3922,6 +3938,46 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-router": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.3.0.tgz",
+ "integrity": "sha512-466f2W7HIWaNXTKM5nHTqNxLrHTyXybm7R0eBlVSt0k/u55tTCDO194OIx/NrYD4TS5SXKTNekXfT37kMKUjgw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookie": "^0.6.0",
+ "cookie": "^1.0.1",
+ "set-cookie-parser": "^2.6.0",
+ "turbo-stream": "2.4.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-router-dom": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.3.0.tgz",
+ "integrity": "sha512-z7Q5FTiHGgQfEurX/FBinkOXhWREJIAB2RiU24lvcBa82PxUpwqvs/PAXb9lJyPjTs2jrl6UkLvCZVGJPeNuuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "react-router": "7.3.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=18",
+ "react-dom": ">=18"
+ }
+ },
"node_modules/reflect.getprototypeof": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -4104,6 +4160,12 @@
"semver": "bin/semver.js"
}
},
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
+ "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
+ "license": "MIT"
+ },
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -4399,6 +4461,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/turbo-stream": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
+ "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
+ "license": "ISC"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index cb61e21..c437e3a 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -22,7 +22,8 @@
"react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0",
- "react-leaflet": "^5.0.0"
+ "react-leaflet": "^5.0.0",
+ "react-router-dom": "^7.3.0"
},
"devDependencies": {
"@eslint/js": "^9.19.0",
diff --git a/frontend/public/config/settings.json b/frontend/public/config/settings.json
index 60a064f..58754ca 100644
--- a/frontend/public/config/settings.json
+++ b/frontend/public/config/settings.json
@@ -16,6 +16,9 @@
"GET_DEVICES": "/devices",
"GET_DEVICE_BY_ID": "/devices/{0}",
"GET_DEVICE_SENSORS": "/devices/{0}/sensors",
+ "GET_DEVICE_LATEST_VALUES": "/devices/{0}/latest",
+ "GET_DEVICE_POLLUTION_MAP": "/devices/{0}/pollution-map",
+ "GET_DEVICE_HISTORY": "/devices/{0}/history",
"POST_DEVICES": "/devices",
"PUT_DEVICE_BY_ID": "/devices/{0}",
"GET_SENSORS": "/sensors",
diff --git a/frontend/src/components/App.jsx b/frontend/src/components/App.jsx
index 00297e0..18ff5b5 100644
--- a/frontend/src/components/App.jsx
+++ b/frontend/src/components/App.jsx
@@ -4,11 +4,13 @@ import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import Home from '../pages/Home.jsx'
+import Dashboard from '../pages/Dashboard.jsx'
import MenuButton from './MenuButton.jsx'
import SideMenu from './SideMenu.jsx'
import ThemeButton from '../components/ThemeButton.jsx'
import Header from '../components/Header.jsx'
+import { Routes, Route } from 'react-router-dom'
import { useState } from 'react'
/**
@@ -51,13 +53,15 @@ const App = () => {
return (
<>
- {/* Planeo añadir un React Router */}
Error al cargar configuración: {configError}
; if (!config) returnConfiguración no disponible.
; - const BASE = config.appConfig.endpoints.baseUrl; + const BASE = config.appConfig.endpoints.BASE_URL; const ENDPOINT = config.appConfig.endpoints.sensors; const reqConfig = { diff --git a/frontend/src/components/PollutionMap.jsx b/frontend/src/components/PollutionMap.jsx index ffc961e..281ef32 100644 --- a/frontend/src/components/PollutionMap.jsx +++ b/frontend/src/components/PollutionMap.jsx @@ -1,4 +1,5 @@ import { MapContainer, TileLayer, Circle, Popup } from 'react-leaflet'; +import PropTypes from 'prop-types'; import { useConfig } from '../contexts/ConfigContext.jsx'; @@ -57,18 +58,19 @@ const PollutionCircles = ({ data }) => { }); }; -const PollutionMap = () => { +const PollutionMap = ({ deviceId }) => { const { config, configLoading, configError } = useConfig(); if (configLoading) returnCargando configuración...
; if (configError) returnError al cargar configuración: {configError}
; if (!config) returnConfiguración no disponible.
; - const BASE = config.appConfig.endpoints.baseUrl; - const ENDPOINT = config.appConfig.endpoints.sensors; + const BASE = config.appConfig.endpoints.BASE_URL; + const ENDPOINT = config.appConfig.endpoints.GET_DEVICE_POLLUTION_MAP; + let endp = ENDPOINT.replace('{0}', deviceId); const reqConfig = { - baseUrl: `${BASE}/${ENDPOINT}`, + baseUrl: `${BASE}/${endp}`, params: {} } @@ -93,10 +95,10 @@ const PollutionMapContent = () => { const SEVILLA = config?.userConfig.city; - const pollutionData = data.map((sensor) => ({ - lat: sensor.lat, - lng: sensor.lon, - level: sensor.value + const pollutionData = data.map((measure) => ({ + lat: measure.lat, + lng: measure.lon, + level: measure.carbonMonoxide })); return ( @@ -118,4 +120,8 @@ const mapStyles = { borderRadius: '20px' }; +PollutionMap.propTypes = { + deviceId: PropTypes.number.isRequired +}; + export default PollutionMap; \ No newline at end of file diff --git a/frontend/src/components/SideMenu.jsx b/frontend/src/components/SideMenu.jsx index ca82867..dd3b16a 100644 --- a/frontend/src/components/SideMenu.jsx +++ b/frontend/src/components/SideMenu.jsx @@ -3,6 +3,14 @@ import PropTypes from 'prop-types'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; +import { DataProvider } from '../contexts/DataContext'; +import { useData } from '../contexts/DataContext'; + +import { useConfig } from '../contexts/ConfigContext'; +import { useTheme } from "../contexts/ThemeContext"; + +import Card from './Card'; + /** ⚠️ EN PRUEBAS ⚠️ * SideMenu.jsx * @@ -24,16 +32,56 @@ import { faTimes } from '@fortawesome/free-solid-svg-icons'; * ⚠️ EN PRUEBAS ⚠️ **/ const SideMenu = ({ isOpen, onClose }) => { + const { config, configLoading, configError } = useConfig(); + + if (configLoading) returnCargando configuración...
; + if (configError) returnError al cargar configuración: {configError}
; + if (!config) returnConfiguración no disponible.
; + + const BASE = config.appConfig.endpoints.BASE_URL; + const ENDPOINT = config.appConfig.endpoints.GET_DEVICES; + + const reqConfig = { + baseUrl: `${BASE}/${ENDPOINT}`, + params: {} + } + return ( -Cargando datos...
; + if (dataError) returnError al cargar datos: {dataError}
; + if (!data) returnDatos no disponibles.
; + + return ( +Cargando configuración...
; if (configError) returnError al cargar configuración: {configError}
; if (!config) returnConfiguración no disponible.
; - const BASE = config.appConfig.endpoints.baseUrl; - const ENDPOINT = config.appConfig.endpoints.sensors; + const BASE = config.appConfig.endpoints.BASE_URL; + const ENDPOINT = config.appConfig.endpoints.GET_DEVICE_LATEST_VALUES; + const endp = ENDPOINT.replace('{0}', deviceId); const reqConfig = { - baseUrl: `${BASE}/${ENDPOINT}`, - params: { - _sort: 'timestamp', - _order: 'desc', - _limit: 1 - } + baseUrl: `${BASE}/${endp}`, + params: {} } return (Cargando datos...
; + if (dataError) returnError al cargar datos: {dataError}
; + if (!data) returnDatos no disponibles.
; const CardsData = [ { id: 1, title: "Temperatura", content: "N/A", status: "Esperando datos...", titleIcon:+ Proyecto universitario para monitorear la calidad del aire usando sensores IoT. +
+ ++ ContaminUS es una solución basada en tecnologías IoT para medir la calidad del aire en tiempo real. + Este proyecto busca crear una herramienta accesible para estudiantes, investigadores y comunidades + interesadas en el monitoreo ambiental. +
+Monitorea la calidad del aire con sensores MQ-135 y DHT11, mostrando datos precisos y actualizados.
+Visualiza los datos de calidad del aire mediante mapas interactivos y gráficos.
+El proyecto está orientado a estudiantes que deseen aprender y colaborar con el análisis de datos ambientales.
+