feat: AnalogInputsChart mit DateRangePicker und vollständiger Redux-Integration erweitert
- 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
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
33
CHANGELOG.md
33
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
|
||||
|
||||
@@ -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<AppDispatch>();
|
||||
|
||||
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(() => {
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.594",
|
||||
"version": "1.6.597",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -2,22 +2,33 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { getAnalogInputsHistoryThunk } from "../thunks/getAnalogInputsHistoryThunk";
|
||||
|
||||
type InputHistoryState = {
|
||||
data: Record<number, any[]>;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
export type AnalogInputsHistoryEntry = {
|
||||
t: string;
|
||||
m: number;
|
||||
};
|
||||
|
||||
const initialState: InputHistoryState = {
|
||||
interface AnalogInputsHistoryState {
|
||||
data: Record<string, AnalogInputsHistoryEntry[]>;
|
||||
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<string>) => {
|
||||
state.zeitraum = action.payload;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
.addCase(getAnalogInputsHistoryThunk.pending, (state) => {
|
||||
@@ -26,8 +37,17 @@ const analogInputsHistorySlice = createSlice({
|
||||
})
|
||||
.addCase(
|
||||
getAnalogInputsHistoryThunk.fulfilled,
|
||||
(state, action: PayloadAction<Record<number, any[]>>) => {
|
||||
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;
|
||||
|
||||
@@ -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 }) => {
|
||||
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 data = await fetchAnalogInputsHistoryService();
|
||||
return data;
|
||||
const response = await fetchAnalogInputsHistory(eingang, zeitraum);
|
||||
return {
|
||||
eingang,
|
||||
zeitraum,
|
||||
daten: response.daten,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return rejectWithValue(error.message || "Unbekannter Fehler");
|
||||
return thunkAPI.rejectWithValue(error.message ?? "Fehler beim Laden");
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,56 +1,24 @@
|
||||
// services/fetchAnalogInputsHistoryService.ts
|
||||
// services/fetchAnalogInputsHistory.ts
|
||||
|
||||
export async function fetchAnalogInputsHistoryService(): Promise<
|
||||
Record<number, unknown[]>
|
||||
> {
|
||||
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")}`;
|
||||
|
||||
const fromDate = formatDate(yesterday);
|
||||
const toDate = formatDate(today);
|
||||
|
||||
const result: Record<number, unknown[]> = {};
|
||||
|
||||
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}`
|
||||
export async function fetchAnalogInputsHistory(
|
||||
eingang: number,
|
||||
zeitraum: string
|
||||
): Promise<{ daten: AnalogInputsHistoryEntry[] }> {
|
||||
const res = await fetch(
|
||||
`/api/cpl/getAnalogInputsHistory?eingang=${eingang}&zeitraum=${zeitraum}`
|
||||
);
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error("Serverantwort war nicht erfolgreich");
|
||||
}
|
||||
|
||||
// ✅ Versuch, JSON zu parsen
|
||||
const data = await response.json();
|
||||
const json = await res.json();
|
||||
return { daten: json.daten }; // Nur das Feld "daten" extrahieren
|
||||
}
|
||||
|
||||
// 🔍 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 {};
|
||||
}
|
||||
}
|
||||
|
||||
// ⬇️ PRODUKTION: direkt vom CPL-Webserver holen
|
||||
/* // ⬇️ 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;
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user