V 1.2.0 diverse Anpassungen, so dass es ähnlich der alten Anwenung ist. Ist nun mal gut zu benutzen
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "wetterstation-frontend",
|
"name": "wetterstation-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.1.0",
|
"version": "1.2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import './App.css'
|
|||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [weatherData, setWeatherData] = useState([])
|
const [weatherData, setWeatherData] = useState([])
|
||||||
|
const [currentWeatherData, setCurrentWeatherData] = useState([]) // Immer die aktuellen 24h-Werte
|
||||||
const [rainData, setRainData] = useState([])
|
const [rainData, setRainData] = useState([])
|
||||||
const [loading, setLoading] = useState(true)
|
const [loading, setLoading] = useState(true)
|
||||||
const [error, setError] = useState(null)
|
const [error, setError] = useState(null)
|
||||||
@@ -18,6 +19,7 @@ function App() {
|
|||||||
// Prüfe ob eingebettete Daten vorhanden sind (statischer Build)
|
// Prüfe ob eingebettete Daten vorhanden sind (statischer Build)
|
||||||
if (window.__WEATHER_DATA__ && timeRange === '24h') {
|
if (window.__WEATHER_DATA__ && timeRange === '24h') {
|
||||||
setWeatherData(window.__WEATHER_DATA__)
|
setWeatherData(window.__WEATHER_DATA__)
|
||||||
|
setCurrentWeatherData(window.__WEATHER_DATA__)
|
||||||
setRainData([])
|
setRainData([])
|
||||||
setLastUpdate(new Date())
|
setLastUpdate(new Date())
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
@@ -58,6 +60,18 @@ function App() {
|
|||||||
const weatherDataResult = await weatherResponse.json()
|
const weatherDataResult = await weatherResponse.json()
|
||||||
setWeatherData(weatherDataResult)
|
setWeatherData(weatherDataResult)
|
||||||
|
|
||||||
|
// Immer die aktuellen 24h-Daten für "Aktuell"-Anzeige laden
|
||||||
|
if (timeRange !== '24h') {
|
||||||
|
const currentUrl = `${baseUrl}/weather/history?hours=24`
|
||||||
|
const currentResponse = await fetch(currentUrl)
|
||||||
|
if (currentResponse.ok) {
|
||||||
|
const currentDataResult = await currentResponse.json()
|
||||||
|
setCurrentWeatherData(currentDataResult)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setCurrentWeatherData(weatherDataResult)
|
||||||
|
}
|
||||||
|
|
||||||
// Regendaten laden (falls separater Endpunkt)
|
// Regendaten laden (falls separater Endpunkt)
|
||||||
if (rainUrl) {
|
if (rainUrl) {
|
||||||
const rainResponse = await fetch(rainUrl)
|
const rainResponse = await fetch(rainUrl)
|
||||||
@@ -138,6 +152,7 @@ function App() {
|
|||||||
<main className="app-main">
|
<main className="app-main">
|
||||||
<WeatherDashboard
|
<WeatherDashboard
|
||||||
data={weatherData}
|
data={weatherData}
|
||||||
|
currentData={currentWeatherData}
|
||||||
rainData={rainData}
|
rainData={rainData}
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
onTimeRangeChange={setTimeRange}
|
onTimeRangeChange={setTimeRange}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.dashboard {
|
.dashboard {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
/* max-width: 1900px; */
|
/* max-width: 1900px; */
|
||||||
max-width: 795px;
|
max-width: 795px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||||||
gap: 0.75rem;
|
gap: 0.75rem;
|
||||||
margin-bottom: 1.5rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.value-card {
|
.value-card {
|
||||||
@@ -82,17 +82,43 @@
|
|||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.chart-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 1rem;
|
padding: 0.5rem;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-container h3 {
|
.chart-container h3 {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 0.75rem;
|
margin-bottom: 0.75rem;
|
||||||
color: #333;
|
color: #333;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
background: #e0e0e0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: -0.5rem -0.5rem 0.5rem -0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-container h3 .unit {
|
||||||
|
font-weight: normal;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-value {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #0066cc;
|
||||||
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chart-wrapper {
|
.chart-wrapper {
|
||||||
@@ -102,11 +128,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.chart-stats {
|
.chart-stats {
|
||||||
margin-top: 0.5rem;
|
/* margin-top: 0.5rem; */
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.85rem;
|
font-size: 0.7rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
background: #e0e0e0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.2rem -0.5rem -0.5rem -0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-footer {
|
.dashboard-footer {
|
||||||
@@ -117,7 +147,7 @@
|
|||||||
.footer-divider {
|
.footer-divider {
|
||||||
border: none;
|
border: none;
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
margin: 0 0 1rem 0;
|
margin: 0 0 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer-credits {
|
.footer-credits {
|
||||||
@@ -155,7 +185,8 @@
|
|||||||
.version-line {
|
.version-line {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 0.5rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-short {
|
.version-short {
|
||||||
@@ -164,6 +195,15 @@
|
|||||||
|
|
||||||
.version-full {
|
.version-full {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-range-short {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-range-full {
|
||||||
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Responsive Design für schmale Bildschirme (Smartphones) */
|
/* Responsive Design für schmale Bildschirme (Smartphones) */
|
||||||
@@ -185,12 +225,21 @@
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.time-range-short {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-range-full {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.time-range-label {
|
.time-range-label {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-short {
|
.version-short {
|
||||||
display: inline;
|
display: inline;
|
||||||
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
.version-full {
|
.version-full {
|
||||||
|
|||||||
@@ -20,12 +20,17 @@ Highcharts.setOptions({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeChange }) => {
|
const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = '24h', onTimeRangeChange }) => {
|
||||||
// Daten vorbereiten und nach Zeit sortieren (älteste zuerst)
|
// Daten vorbereiten und nach Zeit sortieren (älteste zuerst)
|
||||||
const sortedData = useMemo(() => {
|
const sortedData = useMemo(() => {
|
||||||
return [...data].sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
|
return [...data].sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
|
||||||
}, [data])
|
}, [data])
|
||||||
|
|
||||||
|
// Aktuelle Werte aus separaten currentData (immer 24h)
|
||||||
|
const sortedCurrentData = useMemo(() => {
|
||||||
|
return [...currentData].sort((a, b) => new Date(a.datetime) - new Date(b.datetime))
|
||||||
|
}, [currentData])
|
||||||
|
|
||||||
// Zeitraum-Label
|
// Zeitraum-Label
|
||||||
const timeRangeLabel = useMemo(() => {
|
const timeRangeLabel = useMemo(() => {
|
||||||
switch (timeRange) {
|
switch (timeRange) {
|
||||||
@@ -118,7 +123,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
shared: true,
|
shared: true,
|
||||||
crosshairs: true,
|
crosshairs: true,
|
||||||
xDateFormat: timeRange === '24h' ? '%d.%m.%Y %H:%M' : '%d.%m.%Y'
|
xDateFormat: timeRange === '24h' ? '%d.%m.%Y %H:%M' : (timeRange === '7d' || timeRange === '30d' ? '%d.%m.%Y - %Hh' : '%d.%m.%Y')
|
||||||
},
|
},
|
||||||
plotOptions: {
|
plotOptions: {
|
||||||
series: {
|
series: {
|
||||||
@@ -160,7 +165,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
...getCommonOptions(),
|
...getCommonOptions(),
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: { text: 'Temperatur (°C)' },
|
title: { text: null },
|
||||||
min: yMin,
|
min: yMin,
|
||||||
max: yMax
|
max: yMax
|
||||||
},
|
},
|
||||||
@@ -181,6 +186,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 1,
|
||||||
valueSuffix: ' °C'
|
valueSuffix: ' °C'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -192,7 +198,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
...getCommonOptions(),
|
...getCommonOptions(),
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: { text: 'Feuchte (%)' },
|
title: { text: null },
|
||||||
min: 40,
|
min: 40,
|
||||||
max: 100
|
max: 100
|
||||||
},
|
},
|
||||||
@@ -212,6 +218,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 0,
|
||||||
valueSuffix: ' %'
|
valueSuffix: ' %'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -237,7 +244,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
...getCommonOptions(),
|
...getCommonOptions(),
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: { text: 'Luftdruck (hPa)' },
|
title: { text: null },
|
||||||
min: yMin,
|
min: yMin,
|
||||||
max: yMax
|
max: yMax
|
||||||
},
|
},
|
||||||
@@ -257,6 +264,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 0,
|
||||||
valueSuffix: ' hPa'
|
valueSuffix: ' hPa'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -320,7 +328,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
...getCommonOptions(),
|
...getCommonOptions(),
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: { text: yAxisTitle }
|
title: { text: null }
|
||||||
},
|
},
|
||||||
series
|
series
|
||||||
}
|
}
|
||||||
@@ -342,6 +350,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 1,
|
||||||
valueSuffix: ' km/h'
|
valueSuffix: ' km/h'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -357,6 +366,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 1,
|
||||||
valueSuffix: ' km/h'
|
valueSuffix: ' km/h'
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -371,6 +381,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
gapSize: 2 * 24 * 3600 * 1000,
|
gapSize: 2 * 24 * 3600 * 1000,
|
||||||
gapUnit: 'value',
|
gapUnit: 'value',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
|
valueDecimals: 1,
|
||||||
valueSuffix: ' km/h'
|
valueSuffix: ' km/h'
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
@@ -390,12 +401,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: {
|
title: { text: null }
|
||||||
text: 'Windspeed (km/h)',
|
|
||||||
style: {
|
|
||||||
whiteSpace: 'nowrap'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
series
|
series
|
||||||
}
|
}
|
||||||
@@ -404,6 +410,13 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
// Windrichtung Chart
|
// Windrichtung Chart
|
||||||
const windDirOptions = useMemo(() => ({
|
const windDirOptions = useMemo(() => ({
|
||||||
...getCommonOptions(),
|
...getCommonOptions(),
|
||||||
|
tooltip: {
|
||||||
|
formatter: function() {
|
||||||
|
const dateFormat = timeRange === '24h' ? '%d.%m.%Y %H:%M' : (timeRange === '7d' || timeRange === '30d' ? '%d.%m.%Y - %Hh' : '%d.%m.%Y')
|
||||||
|
const dateStr = Highcharts.dateFormat(dateFormat, this.x)
|
||||||
|
return `${dateStr}<br/><span style="color:${this.color}">\u25CF</span> ${this.series.name}: <b>${this.y.toFixed(0)}°</b>`
|
||||||
|
}
|
||||||
|
},
|
||||||
plotOptions: {
|
plotOptions: {
|
||||||
scatter: {
|
scatter: {
|
||||||
marker: {
|
marker: {
|
||||||
@@ -420,7 +433,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
...getCommonOptions().yAxis,
|
...getCommonOptions().yAxis,
|
||||||
title: { text: 'Windrichtung' },
|
title: { text: null },
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 360,
|
max: 360,
|
||||||
tickInterval: 45,
|
tickInterval: 45,
|
||||||
@@ -438,15 +451,12 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
name: 'Windrichtung',
|
name: 'Windrichtung',
|
||||||
data: sortedData.filter(item => item.wind_dir != null).map(item => [new Date(item.datetime).getTime(), item.wind_dir]),
|
data: sortedData.filter(item => item.wind_dir != null).map(item => [new Date(item.datetime).getTime(), item.wind_dir]),
|
||||||
color: 'rgb(54, 162, 235)',
|
color: 'rgb(54, 162, 235)',
|
||||||
type: 'scatter',
|
type: 'scatter'
|
||||||
tooltip: {
|
|
||||||
valueSuffix: ' °'
|
|
||||||
}
|
|
||||||
}]
|
}]
|
||||||
}), [sortedData])
|
}), [sortedData, timeRange])
|
||||||
|
|
||||||
// Aktuellste Werte für Übersicht
|
// Aktuellste Werte für Übersicht (immer aus den 24h-Daten, Fallback auf sortedData)
|
||||||
const current = sortedData[sortedData.length - 1] || {}
|
const current = (sortedCurrentData.length > 0 ? sortedCurrentData[sortedCurrentData.length - 1] : sortedData[sortedData.length - 1]) || {}
|
||||||
|
|
||||||
// Berechne Min/Max für den gewählten Zeitraum
|
// Berechne Min/Max für den gewählten Zeitraum
|
||||||
const periodStats = useMemo(() => {
|
const periodStats = useMemo(() => {
|
||||||
@@ -512,25 +522,29 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
className={timeRange === '24h' ? 'active' : ''}
|
className={timeRange === '24h' ? 'active' : ''}
|
||||||
onClick={() => onTimeRangeChange('24h')}
|
onClick={() => onTimeRangeChange('24h')}
|
||||||
>
|
>
|
||||||
24 Stunden
|
<span className="time-range-full">24 Stunden</span>
|
||||||
|
<span className="time-range-short">24h</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={timeRange === '7d' ? 'active' : ''}
|
className={timeRange === '7d' ? 'active' : ''}
|
||||||
onClick={() => onTimeRangeChange('7d')}
|
onClick={() => onTimeRangeChange('7d')}
|
||||||
>
|
>
|
||||||
7 Tage
|
<span className="time-range-full">7 Tage</span>
|
||||||
|
<span className="time-range-short">7d</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={timeRange === '30d' ? 'active' : ''}
|
className={timeRange === '30d' ? 'active' : ''}
|
||||||
onClick={() => onTimeRangeChange('30d')}
|
onClick={() => onTimeRangeChange('30d')}
|
||||||
>
|
>
|
||||||
30 Tage
|
<span className="time-range-full">30 Tage</span>
|
||||||
|
<span className="time-range-short">30d</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className={timeRange === '365d' ? 'active' : ''}
|
className={timeRange === '365d' ? 'active' : ''}
|
||||||
onClick={() => onTimeRangeChange('365d')}
|
onClick={() => onTimeRangeChange('365d')}
|
||||||
>
|
>
|
||||||
365 Tage
|
<span className="time-range-full">365 Tage</span>
|
||||||
|
<span className="time-range-short">365d</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -541,57 +555,77 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
|
|
||||||
{/* Charts Grid */}
|
{/* Charts Grid */}
|
||||||
<div className="charts-grid">
|
<div className="charts-grid">
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>🌡️ Temperatur{aggregationSuffix} - Aktuell: {current.temperature?.toFixed(1) || '-'}°C</h3>
|
<div className="current-value">Aktuell: {current.temperature?.toFixed(1) || '-'}°C</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={temperatureOptions} />
|
<h3><span>🌡️ Temperatur{aggregationSuffix}</span><span className="unit">[°C]</span></h3>
|
||||||
</div>
|
<div className="chart-wrapper">
|
||||||
<div className="chart-stats">
|
<HighchartsReact highcharts={Highcharts} options={temperatureOptions} />
|
||||||
Min: {periodStats.minTemp?.toFixed(1) || '-'}°C ({periodStats.minTempTime || '-'}) | Max: {periodStats.maxTemp?.toFixed(1) || '-'}°C ({periodStats.maxTempTime || '-'})
|
</div>
|
||||||
|
<div className="chart-stats">
|
||||||
|
Min: {periodStats.minTemp?.toFixed(1) || '-'}°C ({periodStats.minTempTime || '-'}) | Max: {periodStats.maxTemp?.toFixed(1) || '-'}°C ({periodStats.maxTempTime || '-'})
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>🌐 Luftdruck{aggregationSuffix} - Aktuell: {current.pressure?.toFixed(1) || '-'} hPa</h3>
|
<div className="current-value">Aktuell: {current.pressure?.toFixed(0) || '-'} hPa</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={pressureOptions} />
|
<h3><span>🌐 Luftdruck{aggregationSuffix}</span><span className="unit">[hPa]</span></h3>
|
||||||
</div>
|
<div className="chart-wrapper">
|
||||||
<div className="chart-stats">
|
<HighchartsReact highcharts={Highcharts} options={pressureOptions} />
|
||||||
Min: {periodStats.minPressure?.toFixed(1) || '-'} hPa ({periodStats.minPressureTime || '-'}) | Max: {periodStats.maxPressure?.toFixed(1) || '-'} hPa ({periodStats.maxPressureTime || '-'})
|
</div>
|
||||||
|
<div className="chart-stats">
|
||||||
|
Min: {periodStats.minPressure?.toFixed(0) || '-'} hPa ({periodStats.minPressureTime || '-'}) | Max: {periodStats.maxPressure?.toFixed(0) || '-'} hPa ({periodStats.maxPressureTime || '-'})
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>💧 Luftfeuchtigkeit{aggregationSuffix} - Aktuell: {current.humidity || '-'}%</h3>
|
<div className="current-value">Aktuell: {current.humidity || '-'}%</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={humidityOptions} />
|
<h3><span>💧 Luftfeuchtigkeit{aggregationSuffix}</span><span className="unit">[%]</span></h3>
|
||||||
</div>
|
<div className="chart-wrapper">
|
||||||
<div className="chart-stats">
|
<HighchartsReact highcharts={Highcharts} options={humidityOptions} />
|
||||||
Min: {periodStats.minHumidity || '-'}% ({periodStats.minHumidityTime || '-'}) | Max: {periodStats.maxHumidity || '-'}% ({periodStats.maxHumidityTime || '-'})
|
</div>
|
||||||
|
<div className="chart-stats">
|
||||||
|
Min: {periodStats.minHumidity || '-'}% ({periodStats.minHumidityTime || '-'}) | Max: {periodStats.maxHumidity || '-'}% ({periodStats.maxHumidityTime || '-'})
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>🌧️ Regen{aggregationSuffix} - Aktuell: {current.rain?.toFixed(1) || '-'} mm</h3>
|
<div className="current-value">Aktuell: {current.rain?.toFixed(1) || '-'} mm</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={rainOptions} />
|
<h3><span>🌧️ Regen{aggregationSuffix}</span><span className="unit">[mm]</span></h3>
|
||||||
|
<div className="chart-wrapper">
|
||||||
|
<HighchartsReact highcharts={Highcharts} options={rainOptions} />
|
||||||
|
</div>
|
||||||
|
<div className="chart-stats"> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>🧭 Windrichtung{aggregationSuffix} - Aktuell: {current.wind_dir ?? '-'}°</h3>
|
<div className="current-value">Aktuell: {current.wind_dir ?? '-'}°</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={windDirOptions} />
|
<h3><span>🧭 Windrichtung{aggregationSuffix}</span><span className="unit">[°]</span></h3>
|
||||||
|
<div className="chart-wrapper">
|
||||||
|
<HighchartsReact highcharts={Highcharts} options={windDirOptions} />
|
||||||
|
</div>
|
||||||
|
<div className="chart-stats"> </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="chart-container">
|
<div className="chart-item">
|
||||||
<h3>💨 Windspeed{aggregationSuffix} - Aktuell: {current.wind_speed?.toFixed(1) || '-'} km/h</h3>
|
<div className="current-value">Aktuell: {current.wind_speed?.toFixed(1) || '-'} km/h</div>
|
||||||
<div className="chart-wrapper">
|
<div className="chart-container">
|
||||||
<HighchartsReact highcharts={Highcharts} options={windSpeedOptions} />
|
<h3><span>💨 Windspeed{aggregationSuffix}</span><span className="unit">[km/h]</span></h3>
|
||||||
</div>
|
<div className="chart-wrapper">
|
||||||
<div className="chart-stats">
|
<HighchartsReact highcharts={Highcharts} options={windSpeedOptions} />
|
||||||
Max: {periodStats.maxWindGust?.toFixed(1) || '-'} km/h ({periodStats.maxWindGustTime || '-'})
|
</div>
|
||||||
|
<div className="chart-stats">
|
||||||
|
Max: {periodStats.maxWindGust?.toFixed(1) || '-'} km/h ({periodStats.maxWindGustTime || '-'})
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -610,7 +644,7 @@ const WeatherDashboard = ({ data, rainData = [], timeRange = '24h', onTimeRangeC
|
|||||||
{' '}{version} – {buildDate}
|
{' '}{version} – {buildDate}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr />
|
<hr className="footer-divider" />
|
||||||
<div className="footer-credits">
|
<div className="footer-credits">
|
||||||
<div className="footer-left">Daten-Erfassung mit einer Davis VantagePro.</div>
|
<div className="footer-left">Daten-Erfassung mit einer Davis VantagePro.</div>
|
||||||
<div className="footer-right">Grafiken erzeugt mit HighCharts</div>
|
<div className="footer-right">Grafiken erzeugt mit HighCharts</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user