diff --git a/frontend/package.json b/frontend/package.json index 084a74a..d6d960d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "wetterstation-frontend", "private": true, - "version": "1.5.0", + "version": "1.5.1", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/components/WeatherDashboard.jsx b/frontend/src/components/WeatherDashboard.jsx index b8fe1fc..a8074a2 100644 --- a/frontend/src/components/WeatherDashboard.jsx +++ b/frontend/src/components/WeatherDashboard.jsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from 'react' +import { useMemo, useState, useCallback } from 'react' import Highcharts from 'highcharts' import { HighchartsReact } from 'highcharts-react-official' import { format } from 'date-fns' @@ -24,6 +24,24 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' // State für Anleitung const [showAnleitung, setShowAnleitung] = useState(false) + // Schwellwert für Datenlücken (abhängig vom Zeitraum) + const gapThresholdMs = useMemo(() => { + if (timeRange === '24h') return 2 * 60 * 60 * 1000 // 2 Stunden + return 1.5 * 24 * 3600 * 1000 // 1,5 Tage + }, [timeRange]) + + // Fügt null-Einträge in Lücken ein, damit Highcharts die Linie unterbricht + const withGaps = useCallback((pairs) => { + const result = [] + for (let i = 0; i < pairs.length; i++) { + result.push(pairs[i]) + if (i < pairs.length - 1 && pairs[i + 1][0] - pairs[i][0] > gapThresholdMs) { + result.push([(pairs[i][0] + pairs[i + 1][0]) / 2, null]) + } + } + return result + }, [gapThresholdMs]) + // State für benutzerdefinierten Zeitbereich const [showCustomRangeModal, setShowCustomRangeModal] = useState(false) const [customStartDate, setCustomStartDate] = useState('') @@ -405,7 +423,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' series: [ { name: 'Maximaltemperatur', - data: sortedData.filter(item => item.max_temperature != null).map(item => [new Date(item.datetime).getTime(), item.max_temperature]), + data: withGaps(sortedData.filter(item => item.max_temperature != null).map(item => [new Date(item.datetime).getTime(), item.max_temperature])), color: 'rgb(255, 99, 132)', type: 'line', lineWidth: 2, @@ -419,7 +437,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, { name: 'Minimaltemperatur', - data: sortedData.filter(item => item.min_temperature != null).map(item => [new Date(item.datetime).getTime(), item.min_temperature]), + data: withGaps(sortedData.filter(item => item.min_temperature != null).map(item => [new Date(item.datetime).getTime(), item.min_temperature])), color: 'rgb(54, 162, 235)', type: 'line', lineWidth: 2, @@ -461,7 +479,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, series: [{ name: 'Temperatur', - data: sortedData.filter(item => item.temperature != null).map(item => [new Date(item.datetime).getTime(), item.temperature]), + data: withGaps(sortedData.filter(item => item.temperature != null).map(item => [new Date(item.datetime).getTime(), item.temperature])), color: 'rgb(255, 99, 132)', fillColor: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, @@ -481,7 +499,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' } }] } - }, [sortedData, temperatureSuffix, timeRange]) + }, [sortedData, temperatureSuffix, timeRange, withGaps]) // Luftfeuchtigkeit Chart const humidityOptions = useMemo(() => { @@ -497,7 +515,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, series: [{ name: 'Feuchte', - data: sortedData.filter(item => item.humidity != null).map(item => [new Date(item.datetime).getTime(), item.humidity]), + data: withGaps(sortedData.filter(item => item.humidity != null).map(item => [new Date(item.datetime).getTime(), item.humidity])), color: 'rgb(54, 162, 235)', fillColor: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, @@ -516,7 +534,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' } }] } - }, [sortedData, timeRange]) + }, [sortedData, timeRange, withGaps]) // Luftdruck Chart const pressureOptions = useMemo(() => { @@ -545,7 +563,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, series: [{ name: 'Luftdruck', - data: sortedData.filter(item => item.pressure != null).map(item => [new Date(item.datetime).getTime(), item.pressure]), + data: withGaps(sortedData.filter(item => item.pressure != null).map(item => [new Date(item.datetime).getTime(), item.pressure])), color: 'rgb(75, 192, 192)', fillColor: { linearGradient: { x1: 0, y1: 0, x2: 0, y2: 1 }, @@ -564,7 +582,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' } }] } - }, [sortedData, timeRange]) + }, [sortedData, timeRange, withGaps]) // Regen Chart (angepasst an Zeitraum) const rainOptions = useMemo(() => { @@ -645,7 +663,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, series } - }, [sortedData, rainData, timeRange]) + }, [sortedData, rainData, timeRange, withGaps]) // Windgeschwindigkeit Chart const windSpeedOptions = useMemo(() => { @@ -655,9 +673,9 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' const hideGusts = (timeRange === '365d') || (isCustomRange && customDays >= 365) const windSpeedSeries = { name: 'Windgeschwindigkeit', - data: sortedData + data: withGaps(sortedData .filter(item => item.wind_speed != null) - .map(item => [new Date(item.datetime).getTime(), item.wind_speed]), + .map(item => [new Date(item.datetime).getTime(), item.wind_speed])), color: 'rgb(153, 102, 255)', fillColor: 'rgba(153, 102, 255, 0.1)', type: 'area', @@ -674,9 +692,9 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' ? [windSpeedSeries] : [windSpeedSeries, { name: 'Böe' + windGustSuffix, - data: sortedData + data: withGaps(sortedData .filter(item => item.wind_gust != null) - .map(item => [new Date(item.datetime).getTime(), item.wind_gust]), + .map(item => [new Date(item.datetime).getTime(), item.wind_gust])), color: 'rgb(255, 100, 0)', fillColor: 'rgba(255, 100, 0, 0.15)', type: 'area', @@ -716,7 +734,7 @@ const WeatherDashboard = ({ data, currentData = [], rainData = [], timeRange = ' }, series } - }, [sortedData, timeRange, windGustSuffix]) + }, [sortedData, timeRange, windGustSuffix, withGaps]) // Windrichtung Chart const windDirOptions = useMemo(() => ({ diff --git a/restore-db.sh b/restore-db.sh index ec21abf..8c841fb 100755 --- a/restore-db.sh +++ b/restore-db.sh @@ -36,7 +36,10 @@ else exit 1 fi - mapfile -t DUMPS < <(find "$BACKUP_DIR" -name "*.dump" | sort -r) + DUMPS=() + while IFS= read -r line; do + DUMPS+=("$line") + done < <(find "$BACKUP_DIR" -name "*.dump" | sort -r) if [[ ${#DUMPS[@]} -eq 0 ]]; then echo "FEHLER: Keine Backup-Dateien in $BACKUP_DIR gefunden."