1
0

moved ConfigProvider up in the hierarchy (to main.jsx) to wrap the whole App

This commit is contained in:
Jose
2025-02-26 21:08:19 +01:00
parent cf9d5e71fe
commit 40b8f49b89
13 changed files with 104 additions and 36 deletions

3
.gitignore vendored
View File

@@ -25,3 +25,6 @@ frontend/*.sw?
# Do not commit _pycache_ directories # Do not commit _pycache_ directories
backend/__pycache__/ backend/__pycache__/
backend/endpoints/__pycache__/
backend/models/schemas/__pycache__/
backend/models/sql/__pycache__/

View File

@@ -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" DB_URL = "mysql+aiomysql://root:root@localhost:3306/DAD"

View File

@@ -4,4 +4,5 @@ contextlib2
dotenv dotenv
pydantic pydantic
sqlalchemy sqlalchemy
db databases
aiomysql

View File

@@ -6,6 +6,11 @@
] ]
}, },
"appConfig": { "appConfig": {
"endpoints": {
"baseUrl": "http://localhost:8080/api/v1",
"sensors": "sensors",
"sensor": "sensors/sensor"
},
"historyChartConfig": { "historyChartConfig": {
"timeLabels": [ "timeLabels": [
"08:00", "08:00",

View File

@@ -3,16 +3,13 @@ import 'leaflet/dist/leaflet.css'
import 'bootstrap/dist/css/bootstrap.min.css' import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min.js' import 'bootstrap/dist/js/bootstrap.bundle.min.js'
import { ThemeProvider } from '../contexts/ThemeContext.jsx'
import Home from '../pages/Home.jsx' import Home from '../pages/Home.jsx'
const App = () => { const App = () => {
return ( return (
<> <>
<ThemeProvider> {/* Planeo añadir un React Router */}
<Home /> <Home />
</ThemeProvider>
</> </>
); );
} }

View File

@@ -6,16 +6,28 @@ import PropTypes from "prop-types";
import { useTheme } from "../contexts/ThemeContext.jsx"; import { useTheme } from "../contexts/ThemeContext.jsx";
import { DataProvider, useData } from "../contexts/DataContext.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); ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale, Filler);
const HistoryCharts = () => { const HistoryCharts = () => {
const { config, configLoading, configError } = useConfig();
if (configLoading) return <p>Cargando configuración...</p>;
if (configError) return <p>Error al cargar configuración: {configError}</p>;
if (!config) return <p>Configuración no disponible.</p>;
const BASE = config.appConfig.endpoints.baseUrl;
const ENDPOINT = config.appConfig.endpoints.sensors;
const reqConfig = {
baseUrl: `${BASE}/${ENDPOINT}`,
params: {}
}
return ( return (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors"> <DataProvider config={reqConfig}>
<ConfigProvider> <HistoryChartsContent />
<HistoryChartsContent />
</ConfigProvider>
</DataProvider> </DataProvider>
); );
}; };

View File

@@ -1,7 +1,5 @@
import { MapContainer, TileLayer, Circle, Popup } from 'react-leaflet'; import { MapContainer, TileLayer, Circle, Popup } from 'react-leaflet';
import { ConfigProvider } from '../contexts/ConfigContext.jsx';
import { useConfig } from '../contexts/ConfigContext.jsx'; import { useConfig } from '../contexts/ConfigContext.jsx';
import { DataProvider } from '../contexts/DataContext.jsx'; import { DataProvider } from '../contexts/DataContext.jsx';
@@ -41,23 +39,41 @@ const PollutionCircles = ({ data }) => {
}; };
const PollutionMap = () => { const PollutionMap = () => {
const { config, configLoading, configError } = useConfig();
if (configLoading) return <p>Cargando configuración...</p>;
if (configError) return <p>Error al cargar configuración: {configError}</p>;
if (!config) return <p>Configuración no disponible.</p>;
const BASE = config.appConfig.endpoints.baseUrl;
const ENDPOINT = config.appConfig.endpoints.sensors;
const reqConfig = {
baseUrl: `${BASE}/${ENDPOINT}`,
params: {}
}
return ( return (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors"> <DataProvider config={reqConfig}>
<ConfigProvider> <PollutionMapContent />
<PollutionMapContent />
</ConfigProvider>
</DataProvider> </DataProvider>
); );
}; };
const PollutionMapContent = () => { const PollutionMapContent = () => {
const { config } = useConfig(); const { config, configLoading, configError } = useConfig();
const { data, dataLoading, dataError } = useData();
if (configLoading) return <p>Cargando configuración...</p>;
if (configError) return <p>Error al cargar configuración: {configError}</p>;
if (!config) return <p>Configuración no disponible.</p>;
if (dataLoading) return <p>Cargando datos...</p>;
if (dataError) return <p>Error al cargar datos: {configError}</p>;
if (!data) return <p>Datos no disponibles.</p>;
const SEVILLA = config?.userConfig.city; const SEVILLA = config?.userConfig.city;
const { data, loading } = useData();
if (loading) return <p>Cargando datos...</p>;
const pollutionData = data.map((sensor) => ({ const pollutionData = data.map((sensor) => ({
lat: sensor.lat, lat: sensor.lat,
lng: sensor.lon, lng: sensor.lon,

View File

@@ -4,9 +4,28 @@ import CardContainer from './CardContainer';
import { DataProvider } from '../contexts/DataContext'; import { DataProvider } from '../contexts/DataContext';
import { useData } from '../contexts/DataContext'; import { useData } from '../contexts/DataContext';
import { useConfig } from '../contexts/ConfigContext';
const SummaryCards = () => { const SummaryCards = () => {
const { config, configLoading, configError } = useConfig();
if (configLoading) return <p>Cargando configuración...</p>;
if (configError) return <p>Error al cargar configuración: {configError}</p>;
if (!config) return <p>Configuración no disponible.</p>;
const BASE = config.appConfig.endpoints.baseUrl;
const ENDPOINT = config.appConfig.endpoints.sensors;
const reqConfig = {
baseUrl: `${BASE}/${ENDPOINT}`,
params: {
_sort: 'timestamp',
_order: 'desc'
}
}
return ( return (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors?_sort=timestamp&_order=desc"> <DataProvider config={reqConfig}>
<SummaryCardsContent /> <SummaryCardsContent />
</DataProvider> </DataProvider>
); );

View File

@@ -5,8 +5,8 @@ const ConfigContext = createContext();
export const ConfigProvider = ({ children }) => { export const ConfigProvider = ({ children }) => {
const [config, setConfig] = useState(null); const [config, setConfig] = useState(null);
const [loading, setLoading] = useState(true); const [configLoading, setLoading] = useState(true);
const [error, setError] = useState(null); const [configError, setError] = useState(null);
useEffect(() => { useEffect(() => {
const fetchConfig = async () => { const fetchConfig = async () => {
@@ -26,7 +26,7 @@ export const ConfigProvider = ({ children }) => {
}, []); }, []);
return ( return (
<ConfigContext.Provider value={{ config, loading, error }}> <ConfigContext.Provider value={{ config, configLoading, configError }}>
{children} {children}
</ConfigContext.Provider> </ConfigContext.Provider>
); );

View File

@@ -3,15 +3,17 @@ import PropTypes from "prop-types";
const DataContext = createContext(); const DataContext = createContext();
export const DataProvider = ({ children, apiUrl }) => { export const DataProvider = ({ children, config }) => {
const [data, setData] = useState(null); const [data, setData] = useState(null);
const [loading, setLoading] = useState(true); const [dataLoading, setLoading] = useState(true);
const [error, setError] = useState(null); const [dataError, setError] = useState(null);
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
try { 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"); if (!response.ok) throw new Error("Error al obtener datos");
const result = await response.json(); const result = await response.json();
setData(result); setData(result);
@@ -23,10 +25,10 @@ export const DataProvider = ({ children, apiUrl }) => {
}; };
fetchData(); fetchData();
}, [apiUrl]); }, [config]);
return ( return (
<DataContext.Provider value={{ data, loading, error }}> <DataContext.Provider value={{ data, dataLoading, dataError }}>
{children} {children}
</DataContext.Provider> </DataContext.Provider>
); );
@@ -34,7 +36,10 @@ export const DataProvider = ({ children, apiUrl }) => {
DataProvider.propTypes = { DataProvider.propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
apiUrl: PropTypes.string.isRequired, config: PropTypes.shape({
baseUrl: PropTypes.string.isRequired,
params: PropTypes.object,
}).isRequired,
}; };
export const useData = () => useContext(DataContext); export const useData = () => useContext(DataContext);

View File

@@ -39,6 +39,7 @@
color: var(--primary-color); color: var(--primary-color);
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 1px; letter-spacing: 1px;
font-weight: 600;
} }
p.card-text { p.card-text {

View File

@@ -3,8 +3,15 @@ import { createRoot } from 'react-dom/client'
import './css/index.css' import './css/index.css'
import App from './components/App.jsx' import App from './components/App.jsx'
import { ThemeProvider } from './contexts/ThemeContext.jsx'
import { ConfigProvider } from './contexts/ConfigContext.jsx'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(
<StrictMode> <StrictMode>
<App /> <ThemeProvider>
<ConfigProvider>
<App />
</ConfigProvider>
</ThemeProvider>
</StrictMode>, </StrictMode>,
) )

View File

@@ -12,7 +12,6 @@ const Home = () => {
<Dashboard> <Dashboard>
<SummaryCards /> <SummaryCards />
<PollutionMap /> <PollutionMap />
{/* */}
<HistoryCharts /> <HistoryCharts />
</Dashboard> </Dashboard>
<ThemeButton /> <ThemeButton />