diff --git a/.gitignore b/.gitignore index e3f27ab..95cb8ab 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ frontend/*.sln frontend/*.sw? # Do not commit _pycache_ directories -backend/__pycache__/ \ No newline at end of file +backend/__pycache__/ +backend/endpoints/__pycache__/ +backend/models/schemas/__pycache__/ +backend/models/sql/__pycache__/ \ No newline at end of file diff --git a/backend/.env b/backend/.env index d646ae9..0822967 100644 --- a/backend/.env +++ b/backend/.env @@ -1 +1,4 @@ +# esto esta aqui porque es un entorno local +# y además un usuario y contraseña bastante +# genéricos DB_URL = "mysql+aiomysql://root:root@localhost:3306/DAD" \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt index 5c55989..d318c19 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -4,4 +4,5 @@ contextlib2 dotenv pydantic sqlalchemy -db \ No newline at end of file +databases +aiomysql diff --git a/frontend/public/config/settings.json b/frontend/public/config/settings.json index cb18d28..86ae650 100644 --- a/frontend/public/config/settings.json +++ b/frontend/public/config/settings.json @@ -6,6 +6,11 @@ ] }, "appConfig": { + "endpoints": { + "baseUrl": "http://localhost:8080/api/v1", + "sensors": "sensors", + "sensor": "sensors/sensor" + }, "historyChartConfig": { "timeLabels": [ "08:00", diff --git a/frontend/src/components/App.jsx b/frontend/src/components/App.jsx index 6acf5b8..4a63281 100644 --- a/frontend/src/components/App.jsx +++ b/frontend/src/components/App.jsx @@ -3,16 +3,13 @@ import 'leaflet/dist/leaflet.css' import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/js/bootstrap.bundle.min.js' -import { ThemeProvider } from '../contexts/ThemeContext.jsx' - import Home from '../pages/Home.jsx' const App = () => { return ( <> - - - + {/* Planeo añadir un React Router */} + ); } diff --git a/frontend/src/components/HistoryCharts.jsx b/frontend/src/components/HistoryCharts.jsx index eea2fb6..26c0015 100644 --- a/frontend/src/components/HistoryCharts.jsx +++ b/frontend/src/components/HistoryCharts.jsx @@ -6,16 +6,28 @@ import PropTypes from "prop-types"; import { useTheme } from "../contexts/ThemeContext.jsx"; import { DataProvider, useData } from "../contexts/DataContext.jsx"; -import { ConfigProvider, useConfig } from "../contexts/ConfigContext.jsx"; +import { useConfig } from "../contexts/ConfigContext.jsx"; ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale, Filler); const HistoryCharts = () => { + const { config, configLoading, configError } = useConfig(); + + if (configLoading) return

Cargando configuración...

; + if (configError) return

Error al cargar configuración: {configError}

; + if (!config) return

Configuración no disponible.

; + + const BASE = config.appConfig.endpoints.baseUrl; + const ENDPOINT = config.appConfig.endpoints.sensors; + + const reqConfig = { + baseUrl: `${BASE}/${ENDPOINT}`, + params: {} + } + return ( - - - - + + ); }; diff --git a/frontend/src/components/PollutionMap.jsx b/frontend/src/components/PollutionMap.jsx index 514ae00..2dd8a31 100644 --- a/frontend/src/components/PollutionMap.jsx +++ b/frontend/src/components/PollutionMap.jsx @@ -1,7 +1,5 @@ import { MapContainer, TileLayer, Circle, Popup } from 'react-leaflet'; - -import { ConfigProvider } from '../contexts/ConfigContext.jsx'; import { useConfig } from '../contexts/ConfigContext.jsx'; import { DataProvider } from '../contexts/DataContext.jsx'; @@ -41,23 +39,41 @@ const PollutionCircles = ({ data }) => { }; const PollutionMap = () => { + const { config, configLoading, configError } = useConfig(); + + if (configLoading) return

Cargando configuración...

; + if (configError) return

Error al cargar configuración: {configError}

; + if (!config) return

Configuración no disponible.

; + + const BASE = config.appConfig.endpoints.baseUrl; + const ENDPOINT = config.appConfig.endpoints.sensors; + + const reqConfig = { + baseUrl: `${BASE}/${ENDPOINT}`, + params: {} + } + return ( - - - - + + ); }; const PollutionMapContent = () => { - const { config } = useConfig(); + const { config, configLoading, configError } = useConfig(); + const { data, dataLoading, dataError } = useData(); + + if (configLoading) return

Cargando configuración...

; + if (configError) return

Error al cargar configuración: {configError}

; + if (!config) return

Configuración no disponible.

; + + if (dataLoading) return

Cargando datos...

; + if (dataError) return

Error al cargar datos: {configError}

; + if (!data) return

Datos no disponibles.

; + const SEVILLA = config?.userConfig.city; - const { data, loading } = useData(); - - if (loading) return

Cargando datos...

; - const pollutionData = data.map((sensor) => ({ lat: sensor.lat, lng: sensor.lon, diff --git a/frontend/src/components/SummaryCards.jsx b/frontend/src/components/SummaryCards.jsx index 5a14788..68ef1df 100644 --- a/frontend/src/components/SummaryCards.jsx +++ b/frontend/src/components/SummaryCards.jsx @@ -4,9 +4,28 @@ import CardContainer from './CardContainer'; import { DataProvider } from '../contexts/DataContext'; import { useData } from '../contexts/DataContext'; +import { useConfig } from '../contexts/ConfigContext'; + const SummaryCards = () => { + const { config, configLoading, configError } = useConfig(); + + if (configLoading) return

Cargando configuración...

; + if (configError) return

Error al cargar configuración: {configError}

; + if (!config) return

Configuración no disponible.

; + + const BASE = config.appConfig.endpoints.baseUrl; + const ENDPOINT = config.appConfig.endpoints.sensors; + + const reqConfig = { + baseUrl: `${BASE}/${ENDPOINT}`, + params: { + _sort: 'timestamp', + _order: 'desc' + } + } + return ( - + ); diff --git a/frontend/src/contexts/ConfigContext.jsx b/frontend/src/contexts/ConfigContext.jsx index 94ab968..5cccf8b 100644 --- a/frontend/src/contexts/ConfigContext.jsx +++ b/frontend/src/contexts/ConfigContext.jsx @@ -5,8 +5,8 @@ const ConfigContext = createContext(); export const ConfigProvider = ({ children }) => { const [config, setConfig] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); + const [configLoading, setLoading] = useState(true); + const [configError, setError] = useState(null); useEffect(() => { const fetchConfig = async () => { @@ -26,7 +26,7 @@ export const ConfigProvider = ({ children }) => { }, []); return ( - + {children} ); diff --git a/frontend/src/contexts/DataContext.jsx b/frontend/src/contexts/DataContext.jsx index 6f8526a..7ff3153 100644 --- a/frontend/src/contexts/DataContext.jsx +++ b/frontend/src/contexts/DataContext.jsx @@ -3,15 +3,17 @@ import PropTypes from "prop-types"; const DataContext = createContext(); -export const DataProvider = ({ children, apiUrl }) => { +export const DataProvider = ({ children, config }) => { const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); + const [dataLoading, setLoading] = useState(true); + const [dataError, setError] = useState(null); useEffect(() => { const fetchData = async () => { try { - const response = await fetch(apiUrl); + const queryParams = new URLSearchParams(config.params).toString(); + const url = `${config.baseUrl}?${queryParams}`; + const response = await fetch(url); if (!response.ok) throw new Error("Error al obtener datos"); const result = await response.json(); setData(result); @@ -23,10 +25,10 @@ export const DataProvider = ({ children, apiUrl }) => { }; fetchData(); - }, [apiUrl]); + }, [config]); return ( - + {children} ); @@ -34,7 +36,10 @@ export const DataProvider = ({ children, apiUrl }) => { DataProvider.propTypes = { children: PropTypes.node.isRequired, - apiUrl: PropTypes.string.isRequired, + config: PropTypes.shape({ + baseUrl: PropTypes.string.isRequired, + params: PropTypes.object, + }).isRequired, }; export const useData = () => useContext(DataContext); \ No newline at end of file diff --git a/frontend/src/css/Card.css b/frontend/src/css/Card.css index a722456..975e0ca 100644 --- a/frontend/src/css/Card.css +++ b/frontend/src/css/Card.css @@ -39,6 +39,7 @@ color: var(--primary-color); text-transform: uppercase; letter-spacing: 1px; + font-weight: 600; } p.card-text { diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 20dbf24..f5c9d96 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -3,8 +3,15 @@ import { createRoot } from 'react-dom/client' import './css/index.css' import App from './components/App.jsx' +import { ThemeProvider } from './contexts/ThemeContext.jsx' +import { ConfigProvider } from './contexts/ConfigContext.jsx' + createRoot(document.getElementById('root')).render( - + + + + + , ) diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx index 3717f10..b0e2172 100644 --- a/frontend/src/pages/Home.jsx +++ b/frontend/src/pages/Home.jsx @@ -12,7 +12,6 @@ const Home = () => { - {/* */}