"use client"; // components/main/analogInputs/AnalogInputsChart.tsx import React, { useEffect, useRef } from "react"; import { useDispatch, useSelector } from "react-redux"; import { RootState, AppDispatch } from "@/redux/store"; import { Line, ChartJSOrUndefined } from "react-chartjs-2"; import { Chart as ChartJS, LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend, Filler, TimeScale, TooltipItem, } from "chart.js"; import "chartjs-adapter-date-fns"; import { de } from "date-fns/locale"; import { Listbox } from "@headlessui/react"; import { getAnalogInputsHistoryThunk } from "@/redux/thunks/getAnalogInputsHistoryThunk"; import { setVonDatum, setBisDatum, setZeitraum, setAutoLoad, } from "@/redux/slices/analogInputs/analogInputsHistorySlice"; import { getColor } from "@/utils/colors"; import AnalogInputsDatePicker from "./AnalogInputsDatePicker"; // ✅ Nur die Basis-ChartJS-Module registrieren ChartJS.register( LineElement, PointElement, CategoryScale, LinearScale, Tooltip, Legend, Filler, TimeScale ); export default function AnalogInputsChart() { const dispatch = useDispatch(); const chartRef = useRef | null>(null); // Redux Werte für Chart-Daten const { zeitraum, vonDatum, bisDatum, data, autoLoad, selectedId } = useSelector((state: RootState) => state.analogInputsHistory); const selectedAnalogInput = useSelector( (state: RootState) => state.selectedAnalogInput ); // Redux initiale Datum-Werte const vonDatumRedux = useSelector( (state: RootState) => state.dateRangePicker.vonDatum ); const bisDatumRedux = useSelector( (state: RootState) => state.dateRangePicker.bisDatum ); // ✅ Lokale States für Picker + Zeitraum const [localVonDatum, setLocalVonDatum] = React.useState(vonDatumRedux || ""); const [localBisDatum, setLocalBisDatum] = React.useState(bisDatumRedux || ""); const [localZeitraum, setLocalZeitraum] = React.useState(zeitraum); // Synchronisiere lokale Werte mit Redux (z.B. nach AutoLoad Reset) useEffect(() => { setLocalVonDatum(vonDatumRedux || ""); setLocalBisDatum(bisDatumRedux || ""); setLocalZeitraum(zeitraum); }, [vonDatumRedux, bisDatumRedux, zeitraum]); // Initiale Default-Werte: 30 Tage zurück useEffect(() => { if (!vonDatumRedux || !bisDatumRedux) { const today = new Date(); const toDate = today.toISOString().slice(0, 10); const fromDateObj = new Date(today); fromDateObj.setDate(today.getDate() - 30); const fromDate = fromDateObj.toISOString().slice(0, 10); setLocalVonDatum(fromDate); setLocalBisDatum(toDate); } }, []); // ✅ Nur lokale Änderung beim Picker const handleDateChange = (from: string, to: string) => { setLocalVonDatum(from); setLocalBisDatum(to); }; // ✅ Button → Redux + Fetch triggern const handleFetchData = () => { if (!selectedAnalogInput?.id) return; // Redux aktualisieren dispatch(setVonDatum(localVonDatum)); dispatch(setBisDatum(localBisDatum)); dispatch(setZeitraum(localZeitraum)); // Debug anzeigen console.log( "Fetch-URL:", `/api/cpl/getAnalogInputsHistory?eingang=${selectedAnalogInput.id}&zeitraum=${localZeitraum}&von=${localVonDatum}&bis=${localBisDatum}` ); // Thunk-Fetch mit neuen Werten dispatch( getAnalogInputsHistoryThunk({ eingang: selectedAnalogInput.id, zeitraum: localZeitraum, vonDatum: localVonDatum, bisDatum: localBisDatum, }) ); }; // ✅ Chart-Daten aus Redux filtern (Chart reagiert nur nach Button) 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 = vonDatumRedux ? new Date(vonDatumRedux) : null; const to = bisDatumRedux ? new Date(bisDatumRedux) : null; return (!from || date >= from) && (!to || date <= to); }); const memoizedChartData = React.useMemo(() => { return { datasets: filteredData.length > 0 ? zeitraum === "DIA0" ? [ { label: selectedAnalogInput?.label ? `Messwert (m) ${selectedAnalogInput.label}` : "Messwert (m)", data: filteredData .filter((p) => typeof p.m === "number") .map((p) => ({ x: new Date(p.t), y: p.m })), borderColor: getColor("littwin-blue"), backgroundColor: "rgba(59,130,246,0.3)", borderWidth: 2, pointRadius: 0, tension: 0.1, }, { label: "Minimum (i)", data: filteredData .filter((p) => typeof p.i === "number") .map((p) => ({ x: new Date(p.t), y: p.i })), borderColor: "gray", borderDash: [4, 2], borderWidth: 1, pointRadius: 0, tension: 0.1, }, { label: "Maximum (a)", data: filteredData .filter((p) => typeof p.a === "number") .map((p) => ({ x: new Date(p.t), y: p.a })), borderColor: "gray", borderDash: [4, 2], borderWidth: 1, pointRadius: 0, tension: 0.1, }, ] : [ { label: "Minimum (i)", data: filteredData .filter((p) => typeof p.i === "number") .map((p) => ({ x: new Date(p.t), y: p.i })), borderColor: "gray", borderDash: [4, 2], borderWidth: 1, pointRadius: 0, tension: 0.1, }, { label: "Maximum (a)", data: filteredData .filter((p) => typeof p.a === "number") .map((p) => ({ x: new Date(p.t), y: p.a })), borderColor: "gray", borderDash: [4, 2], borderWidth: 1, pointRadius: 0, tension: 0.1, }, { label: "Durchschnitt (g)", data: filteredData .filter((p) => typeof p.g === "number") .map((p) => ({ x: new Date(p.t), y: p.g })), borderColor: getColor("littwin-blue"), backgroundColor: "rgba(59,130,246,0.3)", borderWidth: 2, pointRadius: 0, tension: 0.1, }, ] : [], }; }, [filteredData, zeitraum, selectedAnalogInput]); const memoizedChartOptions = React.useMemo(() => { return { responsive: true, plugins: { legend: { position: "top" as const }, tooltip: { mode: "index" as const, intersect: false, callbacks: { label: (context: TooltipItem<"line">) => { const label = context.dataset.label || ""; return `${label}: ${context.parsed.y}`; }, title: (items: TooltipItem<"line">[]) => { const date = items[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" }, min: vonDatum ? new Date(vonDatum).getTime() : undefined, max: bisDatum ? new Date(bisDatum).getTime() : undefined, }, y: { title: { display: true, text: `Messwert ${selectedAnalogInput?.unit || ""}`, }, }, }, }; }, [vonDatum, bisDatum, selectedAnalogInput]); // ✅ AutoLoad nur beim ersten Laden useEffect(() => { if (autoLoad && selectedId) { dispatch( getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum, vonDatum, bisDatum, }) ); dispatch(setAutoLoad(false)); } }, [autoLoad, selectedId, dispatch, zeitraum, vonDatum, bisDatum]); // Dynamisches Importieren von chartjs-plugin-zoom nur im Browser useEffect(() => { if (typeof window !== "undefined") { import("chartjs-plugin-zoom").then((module) => { ChartJS.register(module.default); }); } }, []); return (
{/* ✅ Neuer DatePicker mit schönem Styling (lokal, ohne Redux) */} {/* ✅ Zeitraum-Auswahl (Listbox nur lokal) */}
{localZeitraum === "DIA0" ? "Alle Messwerte" : localZeitraum === "DIA1" ? "Stündlich" : "Täglich"} {["DIA0", "DIA1", "DIA2"].map((option) => ( {option === "DIA0" ? "Alle Messwerte" : option === "DIA1" ? "Stündlich" : "Täglich"} ))}
{/* ✅ Button: lädt die Daten & aktualisiert Redux */}
{/* Chart-Anzeige */}
{!selectedAnalogInput?.id ? (
Bitte wählen Sie einen Eingang aus, um die Messkurve anzuzeigen
) : ( )}
); }