**WIP**
This commit is contained in:
@@ -4,35 +4,73 @@ import './App.css'
|
||||
|
||||
function App() {
|
||||
const [weatherData, setWeatherData] = useState([])
|
||||
const [rainData, setRainData] = useState([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState(null)
|
||||
const [lastUpdate, setLastUpdate] = useState(null)
|
||||
const [timeRange, setTimeRange] = useState('24h') // '24h', '7d', '30d', '365d'
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
// Prüfe ob eingebettete Daten vorhanden sind (statischer Build)
|
||||
if (window.__WEATHER_DATA__) {
|
||||
if (window.__WEATHER_DATA__ && timeRange === '24h') {
|
||||
setWeatherData(window.__WEATHER_DATA__)
|
||||
setRainData([])
|
||||
setLastUpdate(new Date())
|
||||
setLoading(false)
|
||||
} else {
|
||||
// Development oder Production: Daten von API holen
|
||||
// Im Development: localhost:8000
|
||||
// Im Production: /api/ (nginx proxy)
|
||||
const apiUrl = import.meta.env.DEV
|
||||
? 'http://localhost:8000/weather/history?hours=24'
|
||||
: '/api/weather/history?hours=24'
|
||||
|
||||
const response = await fetch(apiUrl)
|
||||
if (!response.ok) {
|
||||
throw new Error('API-Fehler: ' + response.status)
|
||||
}
|
||||
const data = await response.json()
|
||||
setWeatherData(data)
|
||||
setLastUpdate(new Date())
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
// API-URLs basierend auf Zeitraum
|
||||
let weatherUrl, rainUrl
|
||||
const baseUrl = import.meta.env.DEV ? 'http://localhost:8000' : '/api'
|
||||
|
||||
switch (timeRange) {
|
||||
case '24h':
|
||||
weatherUrl = `${baseUrl}/weather/history?hours=24`
|
||||
rainUrl = null
|
||||
break
|
||||
case '7d':
|
||||
weatherUrl = `${baseUrl}/weather/hourly-aggregated?days=7`
|
||||
rainUrl = `${baseUrl}/weather/rain-daily?days=7`
|
||||
break
|
||||
case '30d':
|
||||
weatherUrl = `${baseUrl}/weather/hourly-aggregated?days=30`
|
||||
rainUrl = `${baseUrl}/weather/rain-daily?days=30`
|
||||
break
|
||||
case '365d':
|
||||
weatherUrl = `${baseUrl}/weather/daily-aggregated?days=365`
|
||||
rainUrl = `${baseUrl}/weather/rain-weekly?days=365`
|
||||
break
|
||||
default:
|
||||
weatherUrl = `${baseUrl}/weather/history?hours=24`
|
||||
rainUrl = null
|
||||
}
|
||||
|
||||
// Wetterdaten laden
|
||||
const weatherResponse = await fetch(weatherUrl)
|
||||
if (!weatherResponse.ok) {
|
||||
throw new Error('API-Fehler: ' + weatherResponse.status)
|
||||
}
|
||||
const weatherDataResult = await weatherResponse.json()
|
||||
setWeatherData(weatherDataResult)
|
||||
|
||||
// Regendaten laden (falls separater Endpunkt)
|
||||
if (rainUrl) {
|
||||
const rainResponse = await fetch(rainUrl)
|
||||
if (rainResponse.ok) {
|
||||
const rainDataResult = await rainResponse.json()
|
||||
setRainData(rainDataResult)
|
||||
}
|
||||
} else {
|
||||
setRainData([])
|
||||
}
|
||||
|
||||
setLastUpdate(new Date())
|
||||
setLoading(false)
|
||||
} catch (err) {
|
||||
setError(err.message)
|
||||
setLoading(false)
|
||||
@@ -41,12 +79,12 @@ function App() {
|
||||
|
||||
fetchData()
|
||||
|
||||
// Automatisches Update alle 5 Minuten (nur im Entwicklungsmodus)
|
||||
if (!window.__WEATHER_DATA__) {
|
||||
// Automatisches Update alle 5 Minuten (nur für 24h und ohne statische Daten)
|
||||
if (!window.__WEATHER_DATA__ && timeRange === '24h') {
|
||||
const interval = setInterval(fetchData, 5 * 60 * 1000)
|
||||
return () => clearInterval(interval)
|
||||
}
|
||||
}, [])
|
||||
}, [timeRange])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -98,7 +136,12 @@ function App() {
|
||||
</header>
|
||||
|
||||
<main className="app-main">
|
||||
<WeatherDashboard data={weatherData} />
|
||||
<WeatherDashboard
|
||||
data={weatherData}
|
||||
rainData={rainData}
|
||||
timeRange={timeRange}
|
||||
onTimeRangeChange={setTimeRange}
|
||||
/>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -4,6 +4,48 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.time-range-nav {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.time-range-nav button {
|
||||
padding: 0.5rem 1.5rem;
|
||||
background: white;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.time-range-nav button:hover {
|
||||
background: #f5f5f5;
|
||||
border-color: #0066cc;
|
||||
}
|
||||
|
||||
.time-range-nav button.active {
|
||||
background: #0066cc;
|
||||
border-color: #0066cc;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.time-range-label {
|
||||
text-align: center;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0.5rem;
|
||||
background: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.current-values {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||
@@ -137,6 +179,15 @@
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.time-range-nav button {
|
||||
padding: 0.4rem 1rem;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.time-range-label {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.version-short {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
@@ -20,63 +20,88 @@ Highcharts.setOptions({
|
||||
}
|
||||
})
|
||||
|
||||
const WeatherDashboard = ({ data }) => {
|
||||
const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeChange }) => {
|
||||
// Daten vorbereiten und nach Zeit sortieren (älteste zuerst)
|
||||
const sortedData = useMemo(() => {
|
||||
return [...data].sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
|
||||
}, [data])
|
||||
|
||||
// Zeitraum-Label
|
||||
const timeRangeLabel = useMemo(() => {
|
||||
switch (timeRange) {
|
||||
case '24h': return 'Die letzten 24 Stunden'
|
||||
case '7d': return 'Die letzten 7 Tage'
|
||||
case '30d': return 'Die letzten 30 Tage'
|
||||
case '365d': return 'Die letzten 365 Tage'
|
||||
default: return 'Die letzten 24 Stunden'
|
||||
}
|
||||
}, [timeRange])
|
||||
|
||||
// Gemeinsame Chart-Optionen
|
||||
const getCommonOptions = () => ({
|
||||
chart: {
|
||||
height: '50%',
|
||||
animation: false,
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
accessibility: {
|
||||
enabled: false
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
shared: true,
|
||||
crosshairs: true,
|
||||
xDateFormat: '%d.%m.%Y %H:%M'
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
marker: {
|
||||
enabled: false,
|
||||
states: {
|
||||
hover: {
|
||||
enabled: true,
|
||||
radius: 5
|
||||
// Gemeinsame Chart-Optionen (angepasst an Zeitraum)
|
||||
const getCommonOptions = () => {
|
||||
// X-Achsen-Konfiguration basierend auf Zeitraum
|
||||
let xAxisConfig = {
|
||||
type: 'datetime',
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: 'rgba(0, 0, 0, 0.1)'
|
||||
}
|
||||
|
||||
switch (timeRange) {
|
||||
case '24h':
|
||||
xAxisConfig.tickInterval = 4 * 3600 * 1000 // 4 Stunden
|
||||
xAxisConfig.labels = { format: '{value:%H:%M}', align: 'center' }
|
||||
break
|
||||
case '7d':
|
||||
case '30d':
|
||||
xAxisConfig.labels = { format: '{value:%d.%m}', align: 'center' }
|
||||
break
|
||||
case '365d':
|
||||
xAxisConfig.labels = { format: '{value:%b}', align: 'center' }
|
||||
break
|
||||
}
|
||||
|
||||
return {
|
||||
chart: {
|
||||
height: '50%',
|
||||
animation: false,
|
||||
backgroundColor: 'transparent'
|
||||
},
|
||||
accessibility: {
|
||||
enabled: false
|
||||
},
|
||||
credits: {
|
||||
enabled: false
|
||||
},
|
||||
title: {
|
||||
text: null
|
||||
},
|
||||
legend: {
|
||||
enabled: false
|
||||
},
|
||||
tooltip: {
|
||||
shared: true,
|
||||
crosshairs: true,
|
||||
xDateFormat: timeRange === '24h' ? '%d.%m.%Y %H:%M' : '%d.%m.%Y'
|
||||
},
|
||||
plotOptions: {
|
||||
series: {
|
||||
marker: {
|
||||
enabled: false,
|
||||
states: {
|
||||
hover: {
|
||||
enabled: true,
|
||||
radius: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
tickInterval: 4 * 3600 * 1000, // 4 Stunden in Millisekunden
|
||||
labels: {
|
||||
format: '{value:%H:%M}',
|
||||
align: 'center'
|
||||
},
|
||||
gridLineWidth: 1,
|
||||
gridLineColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
yAxis: {
|
||||
gridLineColor: 'rgba(0, 0, 0, 0.05)'
|
||||
xAxis: xAxisConfig,
|
||||
yAxis: {
|
||||
gridLineColor: 'rgba(0, 0, 0, 0.05)'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Temperatur Chart
|
||||
const temperatureOptions = useMemo(() => {
|
||||
@@ -192,33 +217,68 @@ const WeatherDashboard = ({ data }) => {
|
||||
}
|
||||
}, [sortedData])
|
||||
|
||||
// Regen Chart
|
||||
const rainOptions = useMemo(() => ({
|
||||
...getCommonOptions(),
|
||||
yAxis: {
|
||||
...getCommonOptions().yAxis,
|
||||
title: { text: 'Regen (mm) / Rate (mm/h)' }
|
||||
},
|
||||
series: [{
|
||||
name: 'Regen',
|
||||
data: sortedData.map(item => [new Date(item.datetime).getTime(), item.rain]),
|
||||
color: 'rgb(54, 162, 235)',
|
||||
fillColor: 'rgba(54, 162, 235, 0.3)',
|
||||
type: 'area',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm'
|
||||
}
|
||||
}, {
|
||||
name: 'Regenrate',
|
||||
data: sortedData.map(item => [new Date(item.datetime).getTime(), item.rain_rate]),
|
||||
color: 'rgb(59, 130, 246)',
|
||||
dashStyle: 'Dash',
|
||||
type: 'line',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm/h'
|
||||
}
|
||||
}]
|
||||
}), [sortedData])
|
||||
// Regen Chart (angepasst an Zeitraum)
|
||||
const rainOptions = useMemo(() => {
|
||||
let series = []
|
||||
let yAxisTitle = 'Regen (mm) / Rate (mm/h)'
|
||||
|
||||
if (timeRange === '24h') {
|
||||
// 24h: Area Chart mit Regen und Regenrate
|
||||
yAxisTitle = 'Regen (mm) / Rate (mm/h)'
|
||||
series = [{
|
||||
name: 'Regen',
|
||||
data: sortedData.map(item => [new Date(item.datetime).getTime(), item.rain]),
|
||||
color: 'rgb(54, 162, 235)',
|
||||
fillColor: 'rgba(54, 162, 235, 0.3)',
|
||||
type: 'area',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm'
|
||||
}
|
||||
}, {
|
||||
name: 'Regenrate',
|
||||
data: sortedData.map(item => [new Date(item.datetime).getTime(), item.rain_rate]),
|
||||
color: 'rgb(59, 130, 246)',
|
||||
dashStyle: 'Dash',
|
||||
type: 'line',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm/h'
|
||||
}
|
||||
}]
|
||||
} else if (timeRange === '7d' || timeRange === '30d') {
|
||||
// 7d/30d: Balkendiagramm mit täglichen Summen
|
||||
yAxisTitle = 'Regen (mm pro Tag)'
|
||||
series = [{
|
||||
name: 'Regen',
|
||||
data: rainData.map(item => [new Date(item.date).getTime(), item.total_rain || 0]),
|
||||
color: 'rgb(54, 162, 235)',
|
||||
type: 'column',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm'
|
||||
}
|
||||
}]
|
||||
} else if (timeRange === '365d') {
|
||||
// 365d: Balkendiagramm mit wöchentlichen Summen
|
||||
yAxisTitle = 'Regen (mm pro Woche)'
|
||||
series = [{
|
||||
name: 'Regen',
|
||||
data: rainData.map(item => [new Date(item.week_start).getTime(), item.total_rain || 0]),
|
||||
color: 'rgb(54, 162, 235)',
|
||||
type: 'column',
|
||||
tooltip: {
|
||||
valueSuffix: ' mm'
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
return {
|
||||
...getCommonOptions(),
|
||||
yAxis: {
|
||||
...getCommonOptions().yAxis,
|
||||
title: { text: yAxisTitle }
|
||||
},
|
||||
series
|
||||
}
|
||||
}, [sortedData, rainData, timeRange])
|
||||
|
||||
// Windgeschwindigkeit Chart
|
||||
const windSpeedOptions = useMemo(() => ({
|
||||
@@ -311,66 +371,97 @@ const WeatherDashboard = ({ data }) => {
|
||||
// Aktuellste Werte für Übersicht
|
||||
const current = sortedData[sortedData.length - 1] || {}
|
||||
|
||||
// Berechne Min/Max für den aktuellen Tag
|
||||
const todayStats = useMemo(() => {
|
||||
const now = new Date()
|
||||
const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate())
|
||||
|
||||
const todayData = sortedData.filter(item => {
|
||||
const itemDate = new Date(item.datetime)
|
||||
return itemDate >= todayStart
|
||||
})
|
||||
// Berechne Min/Max für den gewählten Zeitraum
|
||||
const periodStats = useMemo(() => {
|
||||
// Für den gewählten Zeitraum alle Daten verwenden
|
||||
const periodData = sortedData
|
||||
|
||||
if (todayData.length === 0) {
|
||||
if (periodData.length === 0) {
|
||||
return {
|
||||
minTemp: null, maxTemp: null, minTempTime: null, maxTempTime: null,
|
||||
minHumidity: null, maxHumidity: null, minHumidityTime: null, maxHumidityTime: null,
|
||||
minPressure: null, maxPressure: null, minPressureTime: null, maxPressureTime: null
|
||||
}
|
||||
}
|
||||
|
||||
// Zeitformat basierend auf Zeitraum
|
||||
const timeFormat = timeRange === '24h' ? 'HH:mm' : 'dd.MM HH:mm'
|
||||
|
||||
// Temperatur
|
||||
const minTempItem = todayData.reduce((min, item) =>
|
||||
const minTempItem = periodData.reduce((min, item) =>
|
||||
item.temperature != null && (min === null || item.temperature < min.temperature) ? item : min, null)
|
||||
const maxTempItem = todayData.reduce((max, item) =>
|
||||
const maxTempItem = periodData.reduce((max, item) =>
|
||||
item.temperature != null && (max === null || item.temperature > max.temperature) ? item : max, null)
|
||||
|
||||
// Luftfeuchtigkeit
|
||||
const minHumidityItem = todayData.reduce((min, item) =>
|
||||
const minHumidityItem = periodData.reduce((min, item) =>
|
||||
item.humidity != null && (min === null || item.humidity < min.humidity) ? item : min, null)
|
||||
const maxHumidityItem = todayData.reduce((max, item) =>
|
||||
const maxHumidityItem = periodData.reduce((max, item) =>
|
||||
item.humidity != null && (max === null || item.humidity > max.humidity) ? item : max, null)
|
||||
|
||||
// Luftdruck
|
||||
const minPressureItem = todayData.reduce((min, item) =>
|
||||
const minPressureItem = periodData.reduce((min, item) =>
|
||||
item.pressure != null && (min === null || item.pressure < min.pressure) ? item : min, null)
|
||||
const maxPressureItem = todayData.reduce((max, item) =>
|
||||
const maxPressureItem = periodData.reduce((max, item) =>
|
||||
item.pressure != null && (max === null || item.pressure > max.pressure) ? item : max, null)
|
||||
|
||||
// Windgeschwindigkeit
|
||||
const maxWindGustItem = todayData.reduce((max, item) =>
|
||||
const maxWindGustItem = periodData.reduce((max, item) =>
|
||||
item.wind_gust != null && (max === null || item.wind_gust > max.wind_gust) ? item : max, null)
|
||||
|
||||
return {
|
||||
minTemp: minTempItem?.temperature ?? null,
|
||||
maxTemp: maxTempItem?.temperature ?? null,
|
||||
minTempTime: minTempItem ? format(new Date(minTempItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
maxTempTime: maxTempItem ? format(new Date(maxTempItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
minTempTime: minTempItem ? format(new Date(minTempItem.datetime), timeFormat, { locale: de }) : null,
|
||||
maxTempTime: maxTempItem ? format(new Date(maxTempItem.datetime), timeFormat, { locale: de }) : null,
|
||||
minHumidity: minHumidityItem?.humidity ?? null,
|
||||
maxHumidity: maxHumidityItem?.humidity ?? null,
|
||||
minHumidityTime: minHumidityItem ? format(new Date(minHumidityItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
maxHumidityTime: maxHumidityItem ? format(new Date(maxHumidityItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
minHumidityTime: minHumidityItem ? format(new Date(minHumidityItem.datetime), timeFormat, { locale: de }) : null,
|
||||
maxHumidityTime: maxHumidityItem ? format(new Date(maxHumidityItem.datetime), timeFormat, { locale: de }) : null,
|
||||
minPressure: minPressureItem?.pressure ?? null,
|
||||
maxPressure: maxPressureItem?.pressure ?? null,
|
||||
minPressureTime: minPressureItem ? format(new Date(minPressureItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
maxPressureTime: maxPressureItem ? format(new Date(maxPressureItem.datetime), 'HH:mm', { locale: de }) : null,
|
||||
minPressureTime: minPressureItem ? format(new Date(minPressureItem.datetime), timeFormat, { locale: de }) : null,
|
||||
maxPressureTime: maxPressureItem ? format(new Date(maxPressureItem.datetime), timeFormat, { locale: de }) : null,
|
||||
maxWindGust: maxWindGustItem?.wind_gust ?? null,
|
||||
maxWindGustTime: maxWindGustItem ? format(new Date(maxWindGustItem.datetime), 'HH:mm', { locale: de }) : null
|
||||
maxWindGustTime: maxWindGustItem ? format(new Date(maxWindGustItem.datetime), timeFormat, { locale: de }) : null
|
||||
}
|
||||
}, [sortedData])
|
||||
}, [sortedData, timeRange])
|
||||
|
||||
return (
|
||||
<div className="dashboard">
|
||||
{/* Navigation für Zeitraum-Auswahl */}
|
||||
<div className="time-range-nav">
|
||||
<button
|
||||
className={timeRange === '24h' ? 'active' : ''}
|
||||
onClick={() => onTimeRangeChange('24h')}
|
||||
>
|
||||
24 Stunden
|
||||
</button>
|
||||
<button
|
||||
className={timeRange === '7d' ? 'active' : ''}
|
||||
onClick={() => onTimeRangeChange('7d')}
|
||||
>
|
||||
7 Tage
|
||||
</button>
|
||||
<button
|
||||
className={timeRange === '30d' ? 'active' : ''}
|
||||
onClick={() => onTimeRangeChange('30d')}
|
||||
>
|
||||
30 Tage
|
||||
</button>
|
||||
<button
|
||||
className={timeRange === '365d' ? 'active' : ''}
|
||||
onClick={() => onTimeRangeChange('365d')}
|
||||
>
|
||||
365 Tage
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Zeitraum-Beschreibung */}
|
||||
<div className="time-range-label">
|
||||
{timeRangeLabel}
|
||||
</div>
|
||||
|
||||
{/* Charts Grid */}
|
||||
<div className="charts-grid">
|
||||
<div className="chart-container">
|
||||
@@ -379,7 +470,7 @@ const WeatherDashboard = ({ data }) => {
|
||||
<HighchartsReact highcharts={Highcharts} options={temperatureOptions} />
|
||||
</div>
|
||||
<div className="chart-stats">
|
||||
Min: {todayStats.minTemp?.toFixed(1) || '-'}°C ({todayStats.minTempTime || '-'}) | Max: {todayStats.maxTemp?.toFixed(1) || '-'}°C ({todayStats.maxTempTime || '-'})
|
||||
Min: {periodStats.minTemp?.toFixed(1) || '-'}°C ({periodStats.minTempTime || '-'}) | Max: {periodStats.maxTemp?.toFixed(1) || '-'}°C ({periodStats.maxTempTime || '-'})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -389,7 +480,7 @@ const WeatherDashboard = ({ data }) => {
|
||||
<HighchartsReact highcharts={Highcharts} options={pressureOptions} />
|
||||
</div>
|
||||
<div className="chart-stats">
|
||||
Min: {todayStats.minPressure?.toFixed(1) || '-'} hPa ({todayStats.minPressureTime || '-'}) | Max: {todayStats.maxPressure?.toFixed(1) || '-'} hPa ({todayStats.maxPressureTime || '-'})
|
||||
Min: {periodStats.minPressure?.toFixed(1) || '-'} hPa ({periodStats.minPressureTime || '-'}) | Max: {periodStats.maxPressure?.toFixed(1) || '-'} hPa ({periodStats.maxPressureTime || '-'})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -399,7 +490,7 @@ const WeatherDashboard = ({ data }) => {
|
||||
<HighchartsReact highcharts={Highcharts} options={humidityOptions} />
|
||||
</div>
|
||||
<div className="chart-stats">
|
||||
Min: {todayStats.minHumidity || '-'}% ({todayStats.minHumidityTime || '-'}) | Max: {todayStats.maxHumidity || '-'}% ({todayStats.maxHumidityTime || '-'})
|
||||
Min: {periodStats.minHumidity || '-'}% ({periodStats.minHumidityTime || '-'}) | Max: {periodStats.maxHumidity || '-'}% ({periodStats.maxHumidityTime || '-'})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -423,7 +514,7 @@ const WeatherDashboard = ({ data }) => {
|
||||
<HighchartsReact highcharts={Highcharts} options={windSpeedOptions} />
|
||||
</div>
|
||||
<div className="chart-stats">
|
||||
Max: {todayStats.maxWindGust?.toFixed(1) || '-'} km/h ({todayStats.maxWindGustTime || '-'})
|
||||
Max: {periodStats.maxWindGust?.toFixed(1) || '-'} km/h ({periodStats.maxWindGustTime || '-'})
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user