"use client"; import React, { useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState, AppDispatch } from "@/redux/store"; import { Line } from "react-chartjs-2"; import { Chart as ChartJS, LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend, Filler, TimeScale, } from "chart.js"; import type { ChartOptions } from "chart.js"; import "chartjs-adapter-date-fns"; import { de } from "date-fns/locale"; import { getAnalogInputsHistoryThunk } from "@/redux/thunks/getAnalogInputsHistoryThunk"; import { setVonDatum, setBisDatum, setZeitraum, setAutoLoad, } from "@/redux/slices/analogInputs/analogInputsHistorySlice"; import DateRangePicker from "@/components/common/DateRangePicker"; import { Listbox } from "@headlessui/react"; import { getColor } from "@/utils/colors"; ChartJS.register( LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend, Filler, TimeScale ); type AnalogInputHistoryPoint = { t: string; m: number; }; export default function AnalogInputsChart() { useEffect(() => { const loadZoomPlugin = async () => { const zoomPlugin = (await import("chartjs-plugin-zoom")).default; if (!ChartJS.registry.plugins.get("zoom")) { ChartJS.register(zoomPlugin); } }; loadZoomPlugin(); }, []); const dispatch = useDispatch(); const chartRef = useRef(null); const { zeitraum, vonDatum, bisDatum, data, autoLoad, selectedId } = useSelector((state: RootState) => state.analogInputsHistory); const selectedAnalogInput = useSelector( (state: RootState) => state.selectedAnalogInput ); // ✅ Button-Klick → Fetch auslösen const handleFetchData = () => { if (!selectedAnalogInput?.id) return; // Sicherstellen, dass die neuesten Werte aus dem DateRangePicker verwendet werden const latestVonDatum = vonDatum || new Date().toISOString().slice(0, 10); const latestBisDatum = bisDatum || new Date().toISOString().slice(0, 10); dispatch( getAnalogInputsHistoryThunk({ eingang: selectedAnalogInput.id, zeitraum, vonDatum: latestVonDatum, bisDatum: latestBisDatum, }) ); if (chartRef.current) { const chart = chartRef.current; // Aktualisiere die X-Achse basierend auf den neuesten Werten chart.options.scales.x.min = new Date(latestVonDatum).getTime(); chart.options.scales.x.max = new Date(latestBisDatum).getTime(); // Aktualisiere die Daten des Diagramms const chartKey = selectedAnalogInput?.id ? String(selectedAnalogInput.id + 99) : null; const inputData = chartKey ? data[chartKey] ?? [] : []; const filteredData = inputData.filter((point) => { const date = new Date(point.t); const from = new Date(latestVonDatum); const to = new Date(latestBisDatum); return (!from || date >= from) && (!to || date <= to); }); chart.data.datasets = [ { label: selectedAnalogInput?.label ? `Messwerteingang ${selectedAnalogInput.label}` : "Messwerte", data: filteredData.map((point) => ({ x: point.t, y: point.m })), fill: false, borderColor: getColor("littwin-blue"), backgroundColor: "rgba(59,130,246,0.3)", borderWidth: 2, pointRadius: 0, tension: 0.1, }, ]; // Erzwinge ein vollständiges Redraw des Diagramms chart.update("none"); } }; // ✅ Filtere Daten aus Redux const chartKey = selectedAnalogInput?.id ? String(selectedAnalogInput.id + 99) : null; const inputData = chartKey ? data[chartKey] ?? [] : []; // ✅ Zeitbereich anwenden (nur Anzeige gefiltert) const filteredData = inputData.filter((point) => { const date = new Date(point.t); const from = vonDatum ? new Date(vonDatum) : null; const to = bisDatum ? new Date(bisDatum) : null; return (!from || date >= from) && (!to || date <= to); }); useEffect(() => { const today = new Date(); const vor30Tagen = new Date(today); vor30Tagen.setDate(today.getDate() - 30); if (!vonDatum) dispatch(setVonDatum(vor30Tagen.toISOString().slice(0, 10))); if (!bisDatum) dispatch(setBisDatum(today.toISOString().slice(0, 10))); }, [dispatch, vonDatum, bisDatum]); const handleFetchChartData = () => { if (!selectedId) return; dispatch( getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum, vonDatum, bisDatum, }) ); }; const dataKey = selectedId ? String(selectedId + 99) : null; let filteredPoints: AnalogInputHistoryPoint[] = []; if (dataKey && data[dataKey]) { const fromDate = vonDatum ? new Date(vonDatum) : null; const toDate = bisDatum ? new Date(bisDatum) : null; filteredPoints = data[dataKey].filter((p: AnalogInputHistoryPoint) => { const pointDate = new Date(p.t); return ( (!fromDate || pointDate >= fromDate) && (!toDate || pointDate <= toDate) ); }); } const chartData = { datasets: filteredPoints.length > 0 ? [ { label: selectedAnalogInput?.label ? `Messwerteingang ${selectedAnalogInput.label}` : "Messwerte", data: filteredData.map((point) => ({ x: point.t, y: point.m, })), fill: false, borderColor: getColor("littwin-blue"), backgroundColor: "rgba(59,130,246,0.3)", borderWidth: 2, pointRadius: 0, tension: 0.1, }, ] : [], }; const chartOptions = { responsive: true, plugins: { legend: { position: "top" as const }, tooltip: { mode: "index" as const, intersect: false, callbacks: { label: function (context: any) { return `Messwert: ${context.parsed.y}`; }, title: function (tooltipItems: any[]) { const date = tooltipItems[0].parsed.x; return `Zeitpunkt: ${new Date(date).toLocaleString("de-DE")}`; }, }, }, title: { display: true, text: selectedAnalogInput?.label ? `Verlauf: ${selectedAnalogInput.label}` : "Messwert-Verlauf", }, zoom: { pan: { enabled: true, mode: "x" as const }, zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: "x" as const }, }, }, scales: { x: { type: "time" as const, time: { unit: "day" as const, tooltipFormat: "dd.MM.yyyy HH:mm", displayFormats: { day: "dd.MM.yyyy", }, }, adapters: { date: { locale: de } }, title: { display: true, text: "Zeit" }, // ✅ Hier definieren wir den sichtbaren Bereich dynamisch min: vonDatum ? new Date(vonDatum).getTime() : undefined, max: bisDatum ? new Date(bisDatum).getTime() : undefined, }, y: { title: { display: true, text: `Messwert ${selectedAnalogInput?.unit || ""}`, }, }, }, }; // ✅ DateRangePicker Event → Redux-Datum setzen (aber KEIN Fetch!) const handleDateChange = (from: string, to: string) => { dispatch(setVonDatum(from)); dispatch(setBisDatum(to)); }; useEffect(() => { if (autoLoad && selectedId) { dispatch( getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum, vonDatum, bisDatum, }) ); dispatch(setAutoLoad(false)); // ✅ zurücksetzen, sonst endlose Schleife } }, [autoLoad, selectedId, dispatch, zeitraum, vonDatum, bisDatum]); return (
dispatch(setZeitraum(v))}>
{zeitraum === "DIA0" ? "Alle Messwerte" : zeitraum === "DIA1" ? "Stündlich" : "Täglich"} {["DIA0", "DIA1", "DIA2"].map((option) => ( {option === "DIA0" ? "Alle Messwerte" : option === "DIA1" ? "Stündlich" : "Täglich"} ))}
); }