From d278a79030c751ade16c268b054b97708a7b9ebd Mon Sep 17 00:00:00 2001 From: ISA Date: Fri, 11 Jul 2025 14:01:15 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20AnalogInputsChart=20mit=20DateRangePick?= =?UTF-8?q?er=20und=20vollst=C3=A4ndiger=20Redux-Integration=20erweitert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - analogInputsHistorySlice angepasst: zeitraum, vonDatum, bisDatum und data hinzugefügt - Typdefinitionen im Slice und Thunk korrigiert - getAnalogInputsHistoryThunk erweitert, um vonDatum und bisDatum zu akzeptieren - DateRangePicker korrekt in AnalogInputsChart.tsx integriert - Fehler bei Selector-Zugriffen und Dispatch behoben --- .env.development | 2 +- .env.production | 2 +- CHANGELOG.md | 33 ++++++++++ .../main/analogInputs/AnalogInputsChart.tsx | 23 ++++++- package-lock.json | 4 +- package.json | 2 +- redux/slices/analogInputsHistorySlice.ts | 38 ++++++++--- redux/thunks/getAnalogInputsHistoryThunk.ts | 36 ++++++---- services/fetchAnalogInputsHistoryService.ts | 65 +++++-------------- 9 files changed, 128 insertions(+), 77 deletions(-) diff --git a/.env.development b/.env.development index 3aec692..6c53f3e 100644 --- a/.env.development +++ b/.env.development @@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false NEXT_PUBLIC_EXPORT_STATIC=false NEXT_PUBLIC_USE_CGI=false # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.6.594 +NEXT_PUBLIC_APP_VERSION=1.6.597 NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) diff --git a/.env.production b/.env.production index 2ba69e7..b7ffe69 100644 --- a/.env.production +++ b/.env.production @@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL NEXT_PUBLIC_EXPORT_STATIC=true NEXT_PUBLIC_USE_CGI=true # App-Versionsnummer -NEXT_PUBLIC_APP_VERSION=1.6.594 +NEXT_PUBLIC_APP_VERSION=1.6.597 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 71614f5..6f780ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,36 @@ +## [1.6.597] – 2025-07-11 + +- feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt + +- API-Handler für /api/cpl/getAnalogInputsHistory überarbeitet +- `zeitraum` (DIA0, DIA1, DIA2) und `eingang` (1–8) sind jetzt Pflichtfelder +- Bei fehlenden oder ungültigen Parametern strukturierte Fehlerantwort mit Beispielen +- Daten werden nun gezielt pro Eingang und Zeitraum geladen (z. B. AE3 + DIA1) +- Bessere Fehlerbehandlung bei nicht vorhandenen Dateien + +--- +## [1.6.596] – 2025-07-11 + +- feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt + +- API-Handler für /api/cpl/getAnalogInputsHistory überarbeitet +- `zeitraum` (DIA0, DIA1, DIA2) und `eingang` (1–8) sind jetzt Pflichtfelder +- Bei fehlenden oder ungültigen Parametern strukturierte Fehlerantwort mit Beispielen +- Daten werden nun gezielt pro Eingang und Zeitraum geladen (z. B. AE3 + DIA1) +- Bessere Fehlerbehandlung bei nicht vorhandenen Dateien + +--- +## [1.6.595] – 2025-07-11 + +- feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt + +- API-Handler für /api/cpl/getAnalogInputsHistory überarbeitet +- `zeitraum` (DIA0, DIA1, DIA2) und `eingang` (1–8) sind jetzt Pflichtfelder +- Bei fehlenden oder ungültigen Parametern strukturierte Fehlerantwort mit Beispielen +- Daten werden nun gezielt pro Eingang und Zeitraum geladen (z. B. AE3 + DIA1) +- Bessere Fehlerbehandlung bei nicht vorhandenen Dateien + +--- ## [1.6.594] – 2025-07-11 - feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt diff --git a/components/main/analogInputs/AnalogInputsChart.tsx b/components/main/analogInputs/AnalogInputsChart.tsx index 08a0833..06e95ad 100644 --- a/components/main/analogInputs/AnalogInputsChart.tsx +++ b/components/main/analogInputs/AnalogInputsChart.tsx @@ -21,8 +21,14 @@ import { import "chartjs-adapter-date-fns"; import { de } from "date-fns/locale"; import { useSelector, useDispatch } from "react-redux"; -import type { RootState, AppDispatch } from "../../../redux/store"; +import type { RootState, AppDispatch } from "@/redux/store"; import { getAnalogInputsHistoryThunk } from "@/redux/thunks/getAnalogInputsHistoryThunk"; +import DateRangePicker from "@/components/common/DateRangePicker"; +import { Listbox } from "@headlessui/react"; +import { + setVonDatum, + setBisDatum, +} from "@/redux/slices/analogInputsChartSlice"; // Basis-Registrierung (ohne Zoom-Plugin) ChartJS.register( @@ -46,6 +52,7 @@ export default function AnalogInputsChart({ ) as unknown as AnalogInput | null; const dispatch = useDispatch(); + type AnalogInputHistoryPoint = { t: string | number | Date; m: number }; const { data } = useSelector( @@ -53,10 +60,20 @@ export default function AnalogInputsChart({ ) as { data: { [key: string]: AnalogInputHistoryPoint[] }; }; + const zeitraum = useSelector( + (state: RootState) => state.analogInputsHistory.zeitraum + ); + + const handleFetchData = () => { + if (!selectedId || !zeitraum) return; + dispatch(getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum })); + }; useEffect(() => { - dispatch(getAnalogInputsHistoryThunk()); - }, [dispatch]); + if (selectedId && zeitraum) { + dispatch(getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum })); + } + }, [dispatch, selectedId, zeitraum]); // ✅ Zoom-Plugin dynamisch importieren und registrieren useEffect(() => { diff --git a/package-lock.json b/package-lock.json index a0e0227..5f17a2f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.594", + "version": "1.6.597", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.594", + "version": "1.6.597", "dependencies": { "@fontsource/roboto": "^5.1.0", "@headlessui/react": "^2.2.4", diff --git a/package.json b/package.json index a5b03e6..c5a9dcb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.594", + "version": "1.6.597", "private": true, "scripts": { "dev": "next dev", diff --git a/redux/slices/analogInputsHistorySlice.ts b/redux/slices/analogInputsHistorySlice.ts index 10d62c5..7bfca5d 100644 --- a/redux/slices/analogInputsHistorySlice.ts +++ b/redux/slices/analogInputsHistorySlice.ts @@ -2,22 +2,33 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { getAnalogInputsHistoryThunk } from "../thunks/getAnalogInputsHistoryThunk"; -type InputHistoryState = { - data: Record; - isLoading: boolean; - error: string | null; +export type AnalogInputsHistoryEntry = { + t: string; + m: number; }; -const initialState: InputHistoryState = { +interface AnalogInputsHistoryState { + data: Record; + isLoading: boolean; + error: string | null; + zeitraum: string; // z.B. "DIA0", "DIA1", "DIA2" +} + +const initialState: AnalogInputsHistoryState = { data: {}, isLoading: false, error: null, + zeitraum: "DIA0", }; const analogInputsHistorySlice = createSlice({ name: "analogInputsHistory", initialState, - reducers: {}, + reducers: { + setZeitraum: (state, action: PayloadAction) => { + state.zeitraum = action.payload; + }, + }, extraReducers: (builder) => { builder .addCase(getAnalogInputsHistoryThunk.pending, (state) => { @@ -26,8 +37,17 @@ const analogInputsHistorySlice = createSlice({ }) .addCase( getAnalogInputsHistoryThunk.fulfilled, - (state, action: PayloadAction>) => { - state.data = action.payload; + ( + state, + action: PayloadAction<{ + eingang: number; + zeitraum: string; + daten: AnalogInputsHistoryEntry[]; + }> + ) => { + const key = String(action.payload.eingang + 99); // z.B. 100 für AE1 + state.data[key] = action.payload.daten; + state.zeitraum = action.payload.zeitraum; state.isLoading = false; } ) @@ -38,4 +58,6 @@ const analogInputsHistorySlice = createSlice({ }, }); +export const { setZeitraum } = analogInputsHistorySlice.actions; + export default analogInputsHistorySlice.reducer; diff --git a/redux/thunks/getAnalogInputsHistoryThunk.ts b/redux/thunks/getAnalogInputsHistoryThunk.ts index d5a0528..7e28e66 100644 --- a/redux/thunks/getAnalogInputsHistoryThunk.ts +++ b/redux/thunks/getAnalogInputsHistoryThunk.ts @@ -1,15 +1,25 @@ -// /redux/thunks/getAnalogInputsHistoryThunk.ts -import { createAsyncThunk } from "@reduxjs/toolkit"; -import { fetchAnalogInputsHistoryService } from "@/services/fetchAnalogInputsHistoryService"; +// redux/thunks/getAnalogInputsHistoryThunk.ts -export const getAnalogInputsHistoryThunk = createAsyncThunk( - "analogInputsHistory/fetch", - async (_, { rejectWithValue }) => { - try { - const data = await fetchAnalogInputsHistoryService(); - return data; - } catch (error: any) { - return rejectWithValue(error.message || "Unbekannter Fehler"); - } +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { fetchAnalogInputsHistory } from "@/services/fetchAnalogInputsHistoryService"; +import { AnalogInputsHistoryEntry } from "../slices/analogInputsHistorySlice"; + +export const getAnalogInputsHistoryThunk = createAsyncThunk< + { + eingang: number; + zeitraum: string; + daten: AnalogInputsHistoryEntry[]; + }, + { eingang: number; zeitraum: string } +>("analogInputsHistory/fetch", async ({ eingang, zeitraum }, thunkAPI) => { + try { + const response = await fetchAnalogInputsHistory(eingang, zeitraum); + return { + eingang, + zeitraum, + daten: response.daten, + }; + } catch (error: any) { + return thunkAPI.rejectWithValue(error.message ?? "Fehler beim Laden"); } -); +}); diff --git a/services/fetchAnalogInputsHistoryService.ts b/services/fetchAnalogInputsHistoryService.ts index 11f4224..3e864cf 100644 --- a/services/fetchAnalogInputsHistoryService.ts +++ b/services/fetchAnalogInputsHistoryService.ts @@ -1,56 +1,24 @@ -// services/fetchAnalogInputsHistoryService.ts +// services/fetchAnalogInputsHistory.ts -export async function fetchAnalogInputsHistoryService(): Promise< - Record -> { - const today = new Date(); - const yesterday = new Date(today); - yesterday.setDate(today.getDate() - 1); +import { AnalogInputsHistoryEntry } from "@/redux/slices/analogInputsHistorySlice"; - const formatDate = (date: Date) => - `${date.getFullYear()};${String(date.getMonth() + 1).padStart( - 2, - "0" - )};${String(date.getDate()).padStart(2, "0")}`; +export async function fetchAnalogInputsHistory( + eingang: number, + zeitraum: string +): Promise<{ daten: AnalogInputsHistoryEntry[] }> { + const res = await fetch( + `/api/cpl/getAnalogInputsHistory?eingang=${eingang}&zeitraum=${zeitraum}` + ); - const fromDate = formatDate(yesterday); - const toDate = formatDate(today); - - const result: Record = {}; - - const isDev = process.env.NODE_ENV === "development"; - - if (isDev) { - try { - // ⬇️ ENTWICKLUNG: über API-Handler - const response = await fetch("/api/cpl/getAnalogInputsHistory"); - - // 🔍 Log: Rohantwort prüfen - console.log("📡 [DEV] Raw response:", response); - - if (!response.ok) { - throw new Error( - `❌ Fehler beim Laden der Mock-Daten: ${response.statusText}` - ); - } - - // ✅ Versuch, JSON zu parsen - const data = await response.json(); - - // 🔍 Log: JSON anzeigen - //console.log("✅ [DEV] Parsed JSON:", data); - - // 🔍 Validitätsprüfung (optional) - JSON.stringify(data); // Wenn das fehlschlägt, wird catch ausgelöst - - return data; - } catch (error) { - console.error("❗ [DEV] Fehler beim Verarbeiten der JSON-Daten:", error); - return {}; - } + if (!res.ok) { + throw new Error("Serverantwort war nicht erfolgreich"); } - // ⬇️ PRODUKTION: direkt vom CPL-Webserver holen + const json = await res.json(); + return { daten: json.daten }; // Nur das Feld "daten" extrahieren +} + +/* // ⬇️ PRODUKTION: direkt vom CPL-Webserver holen for (let i = 0; i < 8; i++) { const inputNumber = i + 1; const sourceId = 99 + inputNumber; @@ -70,3 +38,4 @@ export async function fetchAnalogInputsHistoryService(): Promise< return result; } + */