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_EXPORT_STATIC=false
|
||||||
NEXT_PUBLIC_USE_CGI=false
|
NEXT_PUBLIC_USE_CGI=false
|
||||||
# App-Versionsnummer
|
# 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)
|
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_EXPORT_STATIC=true
|
||||||
NEXT_PUBLIC_USE_CGI=true
|
NEXT_PUBLIC_USE_CGI=true
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.594
|
NEXT_PUBLIC_APP_VERSION=1.6.597
|
||||||
NEXT_PUBLIC_CPL_MODE=production
|
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
|
## [1.6.594] – 2025-07-11
|
||||||
|
|
||||||
- feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt
|
- feat(api): Zeitraum und Eingang als Pflichtparameter für AnalogInputs-API eingeführt
|
||||||
|
|||||||
@@ -21,8 +21,14 @@ import {
|
|||||||
import "chartjs-adapter-date-fns";
|
import "chartjs-adapter-date-fns";
|
||||||
import { de } from "date-fns/locale";
|
import { de } from "date-fns/locale";
|
||||||
import { useSelector, useDispatch } from "react-redux";
|
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 { 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)
|
// Basis-Registrierung (ohne Zoom-Plugin)
|
||||||
ChartJS.register(
|
ChartJS.register(
|
||||||
@@ -46,6 +52,7 @@ export default function AnalogInputsChart({
|
|||||||
) as unknown as AnalogInput | null;
|
) as unknown as AnalogInput | null;
|
||||||
|
|
||||||
const dispatch = useDispatch<AppDispatch>();
|
const dispatch = useDispatch<AppDispatch>();
|
||||||
|
|
||||||
type AnalogInputHistoryPoint = { t: string | number | Date; m: number };
|
type AnalogInputHistoryPoint = { t: string | number | Date; m: number };
|
||||||
|
|
||||||
const { data } = useSelector(
|
const { data } = useSelector(
|
||||||
@@ -53,10 +60,20 @@ export default function AnalogInputsChart({
|
|||||||
) as {
|
) as {
|
||||||
data: { [key: string]: AnalogInputHistoryPoint[] };
|
data: { [key: string]: AnalogInputHistoryPoint[] };
|
||||||
};
|
};
|
||||||
|
const zeitraum = useSelector(
|
||||||
|
(state: RootState) => state.analogInputsHistory.zeitraum
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleFetchData = () => {
|
||||||
|
if (!selectedId || !zeitraum) return;
|
||||||
|
dispatch(getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum }));
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(getAnalogInputsHistoryThunk());
|
if (selectedId && zeitraum) {
|
||||||
}, [dispatch]);
|
dispatch(getAnalogInputsHistoryThunk({ eingang: selectedId, zeitraum }));
|
||||||
|
}
|
||||||
|
}, [dispatch, selectedId, zeitraum]);
|
||||||
|
|
||||||
// ✅ Zoom-Plugin dynamisch importieren und registrieren
|
// ✅ Zoom-Plugin dynamisch importieren und registrieren
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.594",
|
"version": "1.6.597",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.594",
|
"version": "1.6.597",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^5.1.0",
|
"@fontsource/roboto": "^5.1.0",
|
||||||
"@headlessui/react": "^2.2.4",
|
"@headlessui/react": "^2.2.4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.594",
|
"version": "1.6.597",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -2,22 +2,33 @@
|
|||||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||||
import { getAnalogInputsHistoryThunk } from "../thunks/getAnalogInputsHistoryThunk";
|
import { getAnalogInputsHistoryThunk } from "../thunks/getAnalogInputsHistoryThunk";
|
||||||
|
|
||||||
type InputHistoryState = {
|
export type AnalogInputsHistoryEntry = {
|
||||||
data: Record<number, any[]>;
|
t: string;
|
||||||
isLoading: boolean;
|
m: number;
|
||||||
error: string | null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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: {},
|
data: {},
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
zeitraum: "DIA0",
|
||||||
};
|
};
|
||||||
|
|
||||||
const analogInputsHistorySlice = createSlice({
|
const analogInputsHistorySlice = createSlice({
|
||||||
name: "analogInputsHistory",
|
name: "analogInputsHistory",
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {},
|
reducers: {
|
||||||
|
setZeitraum: (state, action: PayloadAction<string>) => {
|
||||||
|
state.zeitraum = action.payload;
|
||||||
|
},
|
||||||
|
},
|
||||||
extraReducers: (builder) => {
|
extraReducers: (builder) => {
|
||||||
builder
|
builder
|
||||||
.addCase(getAnalogInputsHistoryThunk.pending, (state) => {
|
.addCase(getAnalogInputsHistoryThunk.pending, (state) => {
|
||||||
@@ -26,8 +37,17 @@ const analogInputsHistorySlice = createSlice({
|
|||||||
})
|
})
|
||||||
.addCase(
|
.addCase(
|
||||||
getAnalogInputsHistoryThunk.fulfilled,
|
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;
|
state.isLoading = false;
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -38,4 +58,6 @@ const analogInputsHistorySlice = createSlice({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const { setZeitraum } = analogInputsHistorySlice.actions;
|
||||||
|
|
||||||
export default analogInputsHistorySlice.reducer;
|
export default analogInputsHistorySlice.reducer;
|
||||||
|
|||||||
@@ -1,15 +1,25 @@
|
|||||||
// /redux/thunks/getAnalogInputsHistoryThunk.ts
|
// redux/thunks/getAnalogInputsHistoryThunk.ts
|
||||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
|
||||||
import { fetchAnalogInputsHistoryService } from "@/services/fetchAnalogInputsHistoryService";
|
|
||||||
|
|
||||||
export const getAnalogInputsHistoryThunk = createAsyncThunk(
|
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||||
"analogInputsHistory/fetch",
|
import { fetchAnalogInputsHistory } from "@/services/fetchAnalogInputsHistoryService";
|
||||||
async (_, { rejectWithValue }) => {
|
import { AnalogInputsHistoryEntry } from "../slices/analogInputsHistorySlice";
|
||||||
try {
|
|
||||||
const data = await fetchAnalogInputsHistoryService();
|
export const getAnalogInputsHistoryThunk = createAsyncThunk<
|
||||||
return data;
|
{
|
||||||
} catch (error: any) {
|
eingang: number;
|
||||||
return rejectWithValue(error.message || "Unbekannter Fehler");
|
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");
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|||||||
@@ -1,56 +1,24 @@
|
|||||||
// services/fetchAnalogInputsHistoryService.ts
|
// services/fetchAnalogInputsHistory.ts
|
||||||
|
|
||||||
export async function fetchAnalogInputsHistoryService(): Promise<
|
import { AnalogInputsHistoryEntry } from "@/redux/slices/analogInputsHistorySlice";
|
||||||
Record<number, unknown[]>
|
|
||||||
> {
|
|
||||||
const today = new Date();
|
|
||||||
const yesterday = new Date(today);
|
|
||||||
yesterday.setDate(today.getDate() - 1);
|
|
||||||
|
|
||||||
const formatDate = (date: Date) =>
|
export async function fetchAnalogInputsHistory(
|
||||||
`${date.getFullYear()};${String(date.getMonth() + 1).padStart(
|
eingang: number,
|
||||||
2,
|
zeitraum: string
|
||||||
"0"
|
): Promise<{ daten: AnalogInputsHistoryEntry[] }> {
|
||||||
)};${String(date.getDate()).padStart(2, "0")}`;
|
const res = await fetch(
|
||||||
|
`/api/cpl/getAnalogInputsHistory?eingang=${eingang}&zeitraum=${zeitraum}`
|
||||||
|
);
|
||||||
|
|
||||||
const fromDate = formatDate(yesterday);
|
if (!res.ok) {
|
||||||
const toDate = formatDate(today);
|
throw new Error("Serverantwort war nicht erfolgreich");
|
||||||
|
|
||||||
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}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ✅ 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 {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ⬇️ 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++) {
|
for (let i = 0; i < 8; i++) {
|
||||||
const inputNumber = i + 1;
|
const inputNumber = i + 1;
|
||||||
const sourceId = 99 + inputNumber;
|
const sourceId = 99 + inputNumber;
|
||||||
@@ -70,3 +38,4 @@ export async function fetchAnalogInputsHistoryService(): Promise<
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user