nun mal erste komplette Version mit API und Frontend (React !)

This commit is contained in:
rxf
2026-02-07 14:12:13 +01:00
parent 6e1f5744f9
commit 9c5f985cab
22 changed files with 1302 additions and 0 deletions

View File

@@ -0,0 +1,367 @@
import { useMemo } from 'react'
import {
Chart as ChartJS,
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler
} from 'chart.js'
import { Line } from 'react-chartjs-2'
import { format } from 'date-fns'
import { de } from 'date-fns/locale'
import './WeatherDashboard.css'
ChartJS.register(
CategoryScale,
LinearScale,
PointElement,
LineElement,
Title,
Tooltip,
Legend,
Filler
)
const WeatherDashboard = ({ data }) => {
// 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])
// Labels für X-Achse (Zeit)
const labels = useMemo(() => {
return sortedData.map(item =>
format(new Date(item.datetime), 'HH:mm', { locale: de })
)
}, [sortedData])
// Chart-Konfiguration
const commonOptions = {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false,
},
elements: {
point: {
radius: 0,
hitRadius: 10,
hoverRadius: 5,
}
},
plugins: {
legend: {
display: false,
},
tooltip: {
callbacks: {
title: (context) => {
const index = context[0].dataIndex
return format(new Date(sortedData[index].datetime), 'dd.MM.yyyy HH:mm', { locale: de })
}
}
}
},
scales: {
x: {
grid: {
display: false,
},
ticks: {
maxTicksLimit: 12,
}
},
y: {
grid: {
color: 'rgba(0, 0, 0, 0.05)',
}
}
}
}
// Temperatur Chart
const temperatureData = {
labels,
datasets: [
{
label: 'Temperatur (°C)',
data: sortedData.map(item => item.temperature),
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
fill: true,
tension: 0.4,
}
]
}
const temperatureOptions = {
...commonOptions,
scales: {
...commonOptions.scales,
y: {
...commonOptions.scales.y,
afterDataLimits: (axis) => {
const range = axis.max - axis.min
if (range < 15) {
const center = (axis.max + axis.min) / 2
axis.max = center + 7.5
axis.min = center - 7.5
}
}
}
}
}
// Feuchte Chart
const humidityData = {
labels,
datasets: [
{
label: 'Luftfeuchtigkeit (%)',
data: sortedData.map(item => item.humidity),
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
fill: true,
tension: 0.4,
}
]
}
const humidityOptions = {
...commonOptions,
scales: {
...commonOptions.scales,
y: {
...commonOptions.scales.y,
min: 0,
max: 100
}
}
}
// Druck Chart
const pressureData = {
labels,
datasets: [
{
label: 'Luftdruck (hPa)',
data: sortedData.map(item => item.pressure),
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.1)',
fill: true,
tension: 0.4,
}
]
}
const pressureOptions = {
...commonOptions,
scales: {
...commonOptions.scales,
y: {
...commonOptions.scales.y,
afterDataLimits: (axis) => {
const range = axis.max - axis.min
if (range < 50) {
const center = (axis.max + axis.min) / 2
axis.max = center + 25
axis.min = center - 25
}
}
}
}
}
// Regen Chart
const rainData = {
labels,
datasets: [
{
label: 'Regen (mm)',
data: sortedData.map(item => item.rain),
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.3)',
fill: true,
tension: 0.4,
},
{
label: 'Regenrate (mm/h)',
data: sortedData.map(item => item.rain_rate),
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderDash: [5, 5],
fill: false,
tension: 0.4,
}
]
}
const rainOptions = {
...commonOptions,
plugins: {
...commonOptions.plugins,
legend: {
display: true,
position: 'top',
}
}
}
// Windgeschwindigkeit Chart
const windSpeedData = {
labels,
datasets: [
{
label: 'Windgeschwindigkeit (km/h)',
data: sortedData.map(item => item.wind_speed),
borderColor: 'rgb(153, 102, 255)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
fill: true,
tension: 0,
},
{
label: 'Windböen (km/h)',
data: sortedData.map(item => item.wind_gust),
borderColor: 'rgb(255, 159, 64)',
backgroundColor: 'rgba(255, 159, 64, 0.1)',
fill: true,
tension: 0,
}
]
}
const windSpeedOptions = {
...commonOptions,
plugins: {
...commonOptions.plugins,
legend: {
display: true,
position: 'top',
}
}
}
// Windrichtung Chart
const windDirData = {
labels,
datasets: [
{
label: 'Windrichtung (°)',
data: sortedData.map(item => item.wind_dir),
borderColor: 'rgb(255, 205, 86)',
backgroundColor: 'rgba(255, 205, 86, 0.1)',
fill: true,
tension: 0,
}
]
}
const windDirOptions = {
...commonOptions,
scales: {
...commonOptions.scales,
y: {
...commonOptions.scales.y,
min: 0,
max: 360,
ticks: {
stepSize: 45,
callback: (value) => {
if (value === 0 || value === 360) return 'N'
if (value === 45) return 'NO'
if (value === 90) return 'O'
if (value === 135) return 'SO'
if (value === 180) return 'S'
if (value === 225) return 'SW'
if (value === 270) return 'W'
if (value === 315) return 'NW'
return ''
}
}
}
}
}
// Aktuellste Werte für Übersicht
const current = sortedData[sortedData.length - 1] || {}
return (
<div className="dashboard">
{/* Aktuelle Werte Übersicht */}
<div className="current-values">
<div className="value-card">
<span className="value-label">Temperatur</span>
<span className="value-number">{current.temperature?.toFixed(1) || '-'}°C</span>
</div>
<div className="value-card">
<span className="value-label">Luftfeuchtigkeit</span>
<span className="value-number">{current.humidity || '-'}%</span>
</div>
<div className="value-card">
<span className="value-label">Luftdruck</span>
<span className="value-number">{current.pressure?.toFixed(1) || '-'} hPa</span>
</div>
<div className="value-card">
<span className="value-label">Wind</span>
<span className="value-number">{current.wind_speed?.toFixed(1) || '-'} km/h</span>
</div>
<div className="value-card">
<span className="value-label">Regen</span>
<span className="value-number">{current.rain?.toFixed(1) || '-'} mm</span>
</div>
</div>
{/* Charts Grid */}
<div className="charts-grid">
<div className="chart-container">
<h3>🌡 Temperatur</h3>
<div className="chart-wrapper">
<Line data={temperatureData} options={temperatureOptions} />
</div>
</div>
<div className="chart-container">
<h3>💧 Luftfeuchtigkeit</h3>
<div className="chart-wrapper">
<Line data={humidityData} options={humidityOptions} />
</div>
</div>
<div className="chart-container">
<h3>🌐 Luftdruck</h3>
<div className="chart-wrapper">
<Line data={pressureData} options={pressureOptions} />
</div>
</div>
<div className="chart-container">
<h3>🌧 Regen</h3>
<div className="chart-wrapper">
<Line data={rainData} options={rainOptions} />
</div>
</div>
<div className="chart-container">
<h3>💨 Windgeschwindigkeit</h3>
<div className="chart-wrapper">
<Line data={windSpeedData} options={windSpeedOptions} />
</div>
</div>
<div className="chart-container">
<h3>🧭 Windrichtung</h3>
<div className="chart-wrapper">
<Line data={windDirData} options={windDirOptions} />
</div>
</div>
</div>
</div>
)
}
export default WeatherDashboard