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
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"

View File

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

View File

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

View File

@@ -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 (
<>
<ThemeProvider>
{/* Planeo añadir un React Router */}
<Home />
</ThemeProvider>
</>
);
}

View File

@@ -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 <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 (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors">
<ConfigProvider>
<DataProvider config={reqConfig}>
<HistoryChartsContent />
</ConfigProvider>
</DataProvider>
);
};

View File

@@ -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 <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 (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors">
<ConfigProvider>
<DataProvider config={reqConfig}>
<PollutionMapContent />
</ConfigProvider>
</DataProvider>
);
};
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 { data, loading } = useData();
if (loading) return <p>Cargando datos...</p>;
const pollutionData = data.map((sensor) => ({
lat: sensor.lat,
lng: sensor.lon,

View File

@@ -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 <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 (
<DataProvider apiUrl="https://contaminus.miarma.net/api/v1/sensors?_sort=timestamp&_order=desc">
<DataProvider config={reqConfig}>
<SummaryCardsContent />
</DataProvider>
);

View File

@@ -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 (
<ConfigContext.Provider value={{ config, loading, error }}>
<ConfigContext.Provider value={{ config, configLoading, configError }}>
{children}
</ConfigContext.Provider>
);

View File

@@ -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 (
<DataContext.Provider value={{ data, loading, error }}>
<DataContext.Provider value={{ data, dataLoading, dataError }}>
{children}
</DataContext.Provider>
);
@@ -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);

View File

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

View File

@@ -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(
<StrictMode>
<ThemeProvider>
<ConfigProvider>
<App />
</ConfigProvider>
</ThemeProvider>
</StrictMode>,
)

View File

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