From cdf5ca6d6e4f72d3875c94d88d513c141af84003 Mon Sep 17 00:00:00 2001 From: ISA Date: Fri, 21 Feb 2025 10:54:15 +0100 Subject: [PATCH] feat: Automatisches Laden der Chart-Daten bei Dropdown-Wechsel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Redux-Slice `kabelueberwachungChartSlice.ts` erweitert um `selectedMode` und `selectedSlotType` - `LoopChartActionBar.tsx` so angepasst, dass Änderungen in den Dropdown-Menüs automatisch `handleFetchData` aufrufen - `useEffect` hinzugefügt, um Daten beim Wechsel von `selectedMode` oder `selectedSlotType` neu zu laden - Manuelles Klicken auf den "Daten Laden"-Button ist nun nicht mehr nötig --- .../LoopChartActionBar.tsx | 50 +++--- .../LoopMeasurementChart.tsx | 160 ++++++------------ config/webVersion.ts | 2 +- redux/slices/dateRangeKueChartSlice.ts | 34 ---- redux/slices/kabelueberwachungChartSlice.ts | 55 ++++++ redux/store.ts | 4 +- 6 files changed, 131 insertions(+), 174 deletions(-) delete mode 100644 redux/slices/dateRangeKueChartSlice.ts create mode 100644 redux/slices/kabelueberwachungChartSlice.ts diff --git a/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx b/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx index 56f7384..0abc836 100644 --- a/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx +++ b/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx @@ -1,43 +1,26 @@ // components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx -import React from "react"; +import React, { useEffect } from "react"; import DateRangePicker from "../DateRangePicker"; import { useDispatch, useSelector } from "react-redux"; import { RootState } from "../../../../../../redux/store"; import { setVonDatum, setBisDatum, -} from "../../../../../../redux/slices/dateRangeKueChartSlice"; -import { setChartData } from "../../../../../../redux/slices/chartDataSlice"; + setChartData, + setSelectedMode, + setSelectedSlotType, +} from "../../../../../../redux/slices/kabelueberwachungChartSlice"; const LoopChartActionBar: React.FC = () => { const dispatch = useDispatch(); - // Datum aus Redux abrufen - const vonDatum = useSelector( - (state: RootState) => state.dateRangeKueChart.vonDatum + // Redux-Status abrufen + const { vonDatum, bisDatum, selectedMode, selectedSlotType } = useSelector( + (state: RootState) => state.kabelueberwachungChart ); - const bisDatum = useSelector( - (state: RootState) => state.dateRangeKueChart.bisDatum - ); - - // Zustand für das Dropdown-Menü zur Auswahl von DIA-Modus (Alle, Stunden, Tage) - const [selectedMode, setSelectedMode] = React.useState< - "DIA0" | "DIA1" | "DIA2" - >("DIA0"); - - // Zustand für das Dropdown-Menü zur Auswahl des Slot-Typs (Isolations- oder Schleifenwiderstand) - const [selectedSlotType, setSelectedSlotType] = React.useState< - "isolationswiderstand" | "schleifenwiderstand" - >("schleifenwiderstand"); - - // Slot-Werte - const isolationswiderstand = 3; - const schleifenwiderstand = 4; /** - * Dynamische API-URL-Erstellung für Entwicklung und Produktion - * @param mode - DIA0, DIA1 oder DIA2 - * @param type - Slot für die Abfrage (3 = Isolationswiderstand, 4 = Schleifenwiderstand) + * API-URL-Erstellung für Entwicklung und Produktion */ const getApiUrl = (mode: "DIA0" | "DIA1" | "DIA2", type: number) => { return process.env.NODE_ENV === "development" @@ -49,7 +32,7 @@ const LoopChartActionBar: React.FC = () => { * Funktion zum Laden der Messwerte */ const handleFetchData = async () => { - const type = selectedSlotType === "schleifenwiderstand" ? 4 : 3; // 4 für Schleifenwiderstand, 3 für Isolationswiderstand + const type = selectedSlotType === "schleifenwiderstand" ? 4 : 3; try { const apiUrl = getApiUrl(selectedMode, type); @@ -71,6 +54,11 @@ const LoopChartActionBar: React.FC = () => { } }; + // **Automatische Datenaktualisierung bei Auswahländerung** + useEffect(() => { + handleFetchData(); + }, [selectedMode, selectedSlotType]); // Wird ausgeführt, wenn sich ein Dropdown ändert + return (
{/* Datumsauswahl */} @@ -83,7 +71,7 @@ const LoopChartActionBar: React.FC = () => { - setSelectedSlotType( - e.target.value as "isolationswiderstand" | "schleifenwiderstand" + dispatch( + setSelectedSlotType( + e.target.value as "isolationswiderstand" | "schleifenwiderstand" + ) ) } className="px-3 py-1 bg-white border rounded text-sm" diff --git a/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx b/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx index 33b55fb..8b5d460 100644 --- a/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx +++ b/components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx @@ -1,6 +1,7 @@ // components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx import React, { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; +import { RootState } from "../../../../../../redux/store"; import Chart from "chart.js/auto"; import "chartjs-adapter-moment"; import zoomPlugin from "chartjs-plugin-zoom"; // Zoom-Plugin importieren @@ -11,16 +12,12 @@ const LoopMeasurementChart = () => { const chartRef = useRef(null); const chartInstance = useRef(null); - // Daten aus Redux abrufen - const chartData = - useSelector( - (state: { chartData: { data: any[] } }) => state.chartData.data - ) ?? []; - - // Ermittlung des ausgewählten Modus (DIA0, DIA1, DIA2) + // Daten & Datum aus Redux abrufen + const { chartData, vonDatum, bisDatum } = useSelector( + (state: RootState) => state.kabelueberwachungChart + ); const selectedMode = useSelector( - (state: { chartMode: { mode: "DIA0" | "DIA1" | "DIA2" } }) => - state.chartMode?.mode ?? "DIA0" + (state: RootState) => state.kabelueberwachungChart.selectedMode ); useEffect(() => { @@ -30,47 +27,52 @@ const LoopMeasurementChart = () => { } console.log("Chart Data:", chartData); + console.log("Von Datum:", vonDatum, "Bis Datum:", bisDatum); + console.log("Selected Mode:", selectedMode); - // Datenvorbereitung: Konvertieren der Zeitstempel in Millisekunden - const processedData = chartData.map((entry) => ({ - ...entry, - timestampMs: new Date(entry.t).getTime(), - })); + // Daten filtern basierend auf vonDatum und bisDatum + const filteredData = chartData.filter((entry) => { + const timestampMs = new Date(entry.t).getTime(); + return ( + timestampMs >= new Date(vonDatum).getTime() && + timestampMs <= new Date(bisDatum).getTime() + ); + }); - // Basis-Datasets für i und a (immer vorhanden) + // Basis-Datasets für i (Minimum) und a (Maximum) const datasets = [ { label: "Messwert Minimum (kOhm)", - data: processedData.map((entry) => ({ - x: entry.timestampMs, + data: filteredData.map((entry) => ({ + x: new Date(entry.t).getTime(), y: entry.i, })), - borderColor: "rgba(75, 192, 192, 1)", + borderColor: "rgba(75, 192, 192, 1)", // Türkis backgroundColor: "rgba(75, 192, 192, 0.2)", fill: false, }, { label: "Messwert Maximum (kOhm)", - data: processedData.map((entry) => ({ - x: entry.timestampMs, + data: filteredData.map((entry) => ({ + x: new Date(entry.t).getTime(), y: entry.a, })), - borderColor: "rgba(192, 75, 75, 1)", + borderColor: "rgba(192, 75, 75, 1)", // Rot backgroundColor: "rgba(192, 75, 75, 0.2)", fill: false, }, ]; - // Falls `m` vorhanden ist (DIA0), wird es gezeichnet + // Falls DIA0: `m` als aktueller Messwert verwenden if ( selectedMode === "DIA0" && - processedData.some((entry) => entry.m !== undefined) + filteredData.some((entry) => entry.m !== undefined) ) { datasets.push({ - label: "Messwert Mittelwert", - data: processedData.map((entry) => ({ - x: entry.timestampMs, - y: entry.m ?? NaN, // Falls `m` nicht existiert, wird `NaN` gesetzt + label: "Messwert Aktuell (m)", + data: filteredData.map((entry) => ({ + x: new Date(entry.t).getTime(), + y: entry.m ?? NaN, })), borderColor: "rgba(255, 165, 0, 1)", // Orange backgroundColor: "rgba(255, 165, 0, 0.2)", @@ -78,30 +80,16 @@ const LoopMeasurementChart = () => { }); } - // Falls `g` vorhanden ist (DIA1/DIA2), wird es gezeichnet - else if ( - selectedMode !== "DIA1" && - processedData.some((entry) => entry.g !== undefined) - ) { - datasets.push({ - label: "Messwert Durchschnitt", - data: processedData.map((entry) => ({ - x: entry.timestampMs, - y: entry.g ?? NaN, // Falls `g` nicht existiert, wird `NaN` gesetzt - })), - borderColor: "rgba(75, 75, 192, 1)", // Blau - backgroundColor: "rgba(75, 75, 192, 0.2)", - fill: false, - }); - } else if ( - selectedMode !== "DIA2" && - processedData.some((entry) => entry.g !== undefined) + // Falls DIA1 oder DIA2: `g` als Durchschnittswert verwenden + if ( + (selectedMode === "DIA1" || selectedMode === "DIA2") && + filteredData.some((entry) => entry.g !== undefined) ) { datasets.push({ label: "Messwert Durchschnitt (g)", - data: processedData.map((entry) => ({ - x: entry.timestampMs, - y: entry.g ?? NaN, // Falls `g` nicht existiert, wird `NaN` gesetzt + data: filteredData.map((entry) => ({ + x: new Date(entry.t).getTime(), + y: entry.g ?? NaN, })), borderColor: "rgba(75, 75, 192, 1)", // Blau backgroundColor: "rgba(75, 75, 192, 0.2)", @@ -118,83 +106,41 @@ const LoopMeasurementChart = () => { responsive: true, maintainAspectRatio: false, elements: { - line: { - spanGaps: true, // Ermöglicht das Zeichnen von Lücken - }, + line: { spanGaps: true }, }, scales: { x: { type: "time", time: { - unit: "minute", - tooltipFormat: "dd.MM.yyyy HH:mm", - displayFormats: { - minute: "dd.MM.yyyy HH:mm", - hour: "dd.MM.yyyy HH:mm", - day: "dd.MM.yyyy", - }, - }, - title: { - display: true, - text: "Zeit (Datum & Uhrzeit)", - }, - ticks: { - autoSkip: false, - maxRotation: 45, - minRotation: 45, - callback: function (value) { - const date = new Date(value); - return `${date.getDate().toString().padStart(2, "0")}.${( - date.getMonth() + 1 - ) - .toString() - .padStart(2, "0")}.${date.getFullYear()} ${date - .getHours() - .toString() - .padStart(2, "0")}:${date - .getMinutes() - .toString() - .padStart(2, "0")}`; - }, + unit: "day", + tooltipFormat: "dd.MM.yyyy", + displayFormats: { day: "dd.MM.yyyy" }, }, + title: { display: true, text: "Zeit (Datum)" }, + min: new Date(vonDatum).getTime(), + max: new Date(bisDatum).getTime(), }, y: { ticks: { - callback: function (value) { - return value.toFixed(2); // Y-Achse Werte mit zwei Dezimalstellen anzeigen - }, + callback: (value) => value.toFixed(2) + " kOhm", }, }, }, plugins: { tooltip: { callbacks: { - label: function (tooltipItem) { - let label = tooltipItem.dataset.label || ""; - if (label) { - label += ": "; - } - if (tooltipItem.raw !== null) { - label += - parseFloat(tooltipItem.raw.y).toFixed(2) + " kOhm"; // Dezimalstellen im Tooltip - } - return label; - }, + label: (tooltipItem) => + `${tooltipItem.dataset.label}: ${tooltipItem.raw.y.toFixed( + 2 + )} kOhm`, }, }, zoom: { - pan: { - enabled: true, - mode: "x", // Nur horizontal scrollen - }, + pan: { enabled: true, mode: "x" }, zoom: { - wheel: { - enabled: true, - }, - pinch: { - enabled: true, - }, - mode: "x", // Nur horizontal zoomen + wheel: { enabled: true }, + pinch: { enabled: true }, + mode: "x", }, }, }, @@ -202,7 +148,7 @@ const LoopMeasurementChart = () => { }); } } - }, [chartData, selectedMode]); + }, [chartData, vonDatum, bisDatum, selectedMode]); return (
diff --git a/config/webVersion.ts b/config/webVersion.ts index cbb2c73..bcba7c5 100644 --- a/config/webVersion.ts +++ b/config/webVersion.ts @@ -6,5 +6,5 @@ 2: Patch oder Hotfix (Bugfixes oder kleine Änderungen). */ -const webVersion = "1.6.76"; +const webVersion = "1.6.77"; export default webVersion; diff --git a/redux/slices/dateRangeKueChartSlice.ts b/redux/slices/dateRangeKueChartSlice.ts deleted file mode 100644 index 33fcb70..0000000 --- a/redux/slices/dateRangeKueChartSlice.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createSlice, PayloadAction } from "@reduxjs/toolkit"; - -interface DateRangeState { - vonDatum: string; - bisDatum: string; -} - -const initialState: DateRangeState = { - vonDatum: "2025;01;01", - bisDatum: "2025;07;31", -}; - -const formatDate = (isoDate: string) => { - const date = new Date(isoDate); - return `${date.getFullYear()};${(date.getMonth() + 1) - .toString() - .padStart(2, "0")};${date.getDate().toString().padStart(2, "0")}`; -}; - -const dateRangeKueChartSlice = createSlice({ - name: "dateRangeKueChart", - initialState, - reducers: { - setVonDatum: (state, action: PayloadAction) => { - state.vonDatum = formatDate(action.payload); - }, - setBisDatum: (state, action: PayloadAction) => { - state.bisDatum = formatDate(action.payload); - }, - }, -}); - -export const { setVonDatum, setBisDatum } = dateRangeKueChartSlice.actions; -export default dateRangeKueChartSlice.reducer; diff --git a/redux/slices/kabelueberwachungChartSlice.ts b/redux/slices/kabelueberwachungChartSlice.ts new file mode 100644 index 0000000..b073390 --- /dev/null +++ b/redux/slices/kabelueberwachungChartSlice.ts @@ -0,0 +1,55 @@ +import { createSlice, PayloadAction } from "@reduxjs/toolkit"; + +interface KabelueberwachungChartState { + chartData: any[]; + vonDatum: string; + bisDatum: string; + selectedMode: "DIA0" | "DIA1" | "DIA2"; + selectedSlotType: "isolationswiderstand" | "schleifenwiderstand"; +} + +const initialState: KabelueberwachungChartState = { + chartData: [], + vonDatum: "2025-02-01", + bisDatum: "2025-02-28", + selectedMode: "DIA0", + selectedSlotType: "schleifenwiderstand", +}; + +const kabelueberwachungChartSlice = createSlice({ + name: "kabelueberwachungChart", + initialState, + reducers: { + setChartData: (state, action: PayloadAction) => { + state.chartData = action.payload; + }, + setVonDatum: (state, action: PayloadAction) => { + state.vonDatum = action.payload; + }, + setBisDatum: (state, action: PayloadAction) => { + state.bisDatum = action.payload; + }, + setSelectedMode: ( + state, + action: PayloadAction<"DIA0" | "DIA1" | "DIA2"> + ) => { + state.selectedMode = action.payload; + }, + setSelectedSlotType: ( + state, + action: PayloadAction<"isolationswiderstand" | "schleifenwiderstand"> + ) => { + state.selectedSlotType = action.payload; + }, + }, +}); + +export const { + setChartData, + setVonDatum, + setBisDatum, + setSelectedMode, + setSelectedSlotType, +} = kabelueberwachungChartSlice.actions; + +export default kabelueberwachungChartSlice.reducer; diff --git a/redux/store.ts b/redux/store.ts index f1fba94..c025fbf 100644 --- a/redux/store.ts +++ b/redux/store.ts @@ -5,7 +5,7 @@ import variablesReducer from "./slices/variablesSlice"; import chartDataReducer from "./slices/chartDataSlice"; import webVersionReducer from "./slices/webVersionSlice"; import digitalInputsReducer from "./slices/digitalInputsSlice"; -import dateRangeKueChartReducer from "./slices/dateRangeKueChartSlice"; +import kabelueberwachungChartReducer from "./slices/kabelueberwachungChartSlice"; const store = configureStore({ reducer: { @@ -14,7 +14,7 @@ const store = configureStore({ chartData: chartDataReducer, webVersion: webVersionReducer, digitalInputs: digitalInputsReducer, - dateRangeKueChart: dateRangeKueChartReducer, + kabelueberwachungChart: kabelueberwachungChartReducer, }, });