feat(analogInputs): automatisches Laden der Chart-Daten bei Tabellenklick via Redux

- analogInputsHistorySlice um `autoLoad` erweitert, um automatisches Laden zu triggern
- handleSelect in AnalogInputsTable dispatcht jetzt `setAutoLoad(true)`
- AnalogInputsChart lauscht auf `autoLoad` + `selectedId` und lädt Daten automatisch
- `autoLoad` wird nach dem Laden wieder auf false zurückgesetzt
This commit is contained in:
Ismail Ali
2025-07-15 09:24:27 +02:00
parent 658aa0cae5
commit f485d87809
8 changed files with 67 additions and 27 deletions

View File

@@ -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.599 NEXT_PUBLIC_APP_VERSION=1.6.601
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)

View File

@@ -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.599 NEXT_PUBLIC_APP_VERSION=1.6.601
NEXT_PUBLIC_CPL_MODE=production NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,13 @@
## [1.6.601] 2025-07-15
- uninstall redux-persist, weil nimmt viel Performance weg
---
## [1.6.600] 2025-07-15
- uninstall redux-persist, weil nimmt viel Performance weg
---
## [1.6.599] 2025-07-14 ## [1.6.599] 2025-07-14
- feat: AnalogInputsChart mit DateRangePicker und vollständiger Redux-Integration erweitert - feat: AnalogInputsChart mit DateRangePicker und vollständiger Redux-Integration erweitert

View File

@@ -22,6 +22,7 @@ import {
setVonDatum, setVonDatum,
setBisDatum, setBisDatum,
setZeitraum, setZeitraum,
setAutoLoad,
} from "@/redux/slices/analogInputs/analogInputsHistorySlice"; } from "@/redux/slices/analogInputs/analogInputsHistorySlice";
import DateRangePicker from "@/components/common/DateRangePicker"; import DateRangePicker from "@/components/common/DateRangePicker";
import { Listbox } from "@headlessui/react"; import { Listbox } from "@headlessui/react";
@@ -43,17 +44,12 @@ type AnalogInputHistoryPoint = {
m: number; m: number;
}; };
export default function AnalogInputsChart({ export default function AnalogInputsChart() {
selectedId,
}: {
selectedId: number | null;
}) {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const chartRef = useRef<any>(null); const chartRef = useRef<any>(null);
const { zeitraum, vonDatum, bisDatum, data, isLoading } = useSelector( const { zeitraum, vonDatum, bisDatum, data, autoLoad, selectedId } =
(state: RootState) => state.analogInputsHistory useSelector((state: RootState) => state.analogInputsHistory);
);
useEffect(() => { useEffect(() => {
const today = new Date(); const today = new Date();
@@ -128,6 +124,19 @@ export default function AnalogInputsChart({
}, },
}; };
useEffect(() => {
if (autoLoad && selectedId) {
dispatch(
getAnalogInputsHistoryThunk({
eingang: selectedId,
zeitraum,
vonDatum,
bisDatum,
})
);
dispatch(setAutoLoad(false)); // ✅ zurücksetzen, sonst endlose Schleife
}
}, [autoLoad, selectedId, dispatch, zeitraum, vonDatum, bisDatum]);
return ( return (
<div className="flex flex-col gap-3"> <div className="flex flex-col gap-3">
<div className="flex flex-wrap items-center gap-4 mb-2"> <div className="flex flex-wrap items-center gap-4 mb-2">

View File

@@ -9,7 +9,10 @@ import settingsIcon from "@iconify/icons-mdi/settings";
import waveformIcon from "@iconify/icons-mdi/waveform"; import waveformIcon from "@iconify/icons-mdi/waveform";
import { setSelectedAnalogInput } from "@/redux/slices/analogInputs/selectedAnalogInputSlice"; import { setSelectedAnalogInput } from "@/redux/slices/analogInputs/selectedAnalogInputSlice";
import { setIsSettingsModalOpen } from "@/redux/slices/analogInputs/analogInputsUiSlice"; import { setIsSettingsModalOpen } from "@/redux/slices/analogInputs/analogInputsUiSlice";
import { setSelectedId } from "@/redux/slices/analogInputs/analogInputsHistorySlice"; import {
setSelectedId,
setAutoLoad,
} from "@/redux/slices/analogInputs/analogInputsHistorySlice";
export default function AnalogInputsTable() { export default function AnalogInputsTable() {
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
@@ -25,10 +28,10 @@ export default function AnalogInputsTable() {
); );
const handleSelect = (id: number, input: AnalogInput) => { const handleSelect = (id: number, input: AnalogInput) => {
dispatch(setIsSettingsModalOpen(true));
dispatch(setSelectedId(id)); dispatch(setSelectedId(id));
setActiveId(id); setActiveId(id);
dispatch(setSelectedAnalogInput(input)); dispatch(setSelectedAnalogInput(input));
dispatch(setAutoLoad(true));
}; };
return ( return (
@@ -54,48 +57,60 @@ export default function AnalogInputsTable() {
<tbody> <tbody>
{Object.values(analogInputs) {Object.values(analogInputs)
.filter( .filter(
(e) => (analogInput) =>
e && typeof e.id === "number" && typeof e.label === "string" analogInput &&
typeof analogInput.id === "number" &&
typeof analogInput.label === "string"
) )
.slice(0, 8) .slice(0, 8)
.map((e, index) => ( .map((analogInput, index) => (
<tr <tr
key={index} key={index}
className={`transition cursor-pointer ${ className={`transition cursor-pointer ${
e.id === activeId ? "bg-blue-100" : "hover:bg-gray-100" analogInput.id === activeId
? "bg-blue-100"
: "hover:bg-gray-100"
}`} }`}
> >
<td <td
className="border p-2" className="border p-2"
onClick={() => handleSelect(e.id!, e)} onClick={() => handleSelect(analogInput.id!, analogInput)}
> >
<div className="flex items-center gap-1 "> <div className="flex items-center gap-1 ">
<Icon <Icon
icon={waveformIcon} icon={waveformIcon}
className="text-gray-600 text-base laptop:text-sm xl:text-sm 2xl:text-lg" className="text-gray-600 text-base laptop:text-sm xl:text-sm 2xl:text-lg"
/> />
{e.id ?? "-"} {analogInput.id ?? "-"}
</div> </div>
</td> </td>
<td <td
className="border p-2 text-right" className="border p-2 text-right"
onClick={() => handleSelect(e.id!, e)} onClick={() => handleSelect(analogInput.id!, analogInput)}
> >
{typeof e.value === "number" ? e.value.toFixed(2) : "-"} {typeof analogInput.value === "number"
? analogInput.value.toFixed(2)
: "-"}
</td> </td>
<td className="border p-2">{e.unit || "-"}</td>
<td <td
className="border p-2" className="border p-2"
onClick={() => handleSelect(e.id!, e)} onClick={() => handleSelect(analogInput.id!, analogInput)}
> >
{e.label || "----"} {analogInput.unit || "-"}
</td>
<td
className="border p-2"
onClick={() => handleSelect(analogInput.id!, analogInput)}
>
{analogInput.label || "----"}
</td> </td>
<td className="border p-2 text-center"> <td className="border p-2 text-center">
<button <button
onClick={() => { onClick={() => {
handleSelect(e.id!, e); handleSelect(analogInput.id!, analogInput);
dispatch(setIsSettingsModalOpen(true));
}} }}
className="text-gray-400 hover:text-gray-500" className="text-gray-400 hover:text-gray-500"
> >

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.599", "version": "1.6.601",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.599", "version": "1.6.601",
"dependencies": { "dependencies": {
"@fontsource/roboto": "^5.1.0", "@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4", "@headlessui/react": "^2.2.4",

View File

@@ -1,6 +1,6 @@
{ {
"name": "cpl-v4", "name": "cpl-v4",
"version": "1.6.599", "version": "1.6.601",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev",

View File

@@ -15,6 +15,7 @@ export interface InputHistoryState {
isLoading: boolean; isLoading: boolean;
error: string | null; error: string | null;
data: Record<string, AnalogInputsHistoryEntry[]>; data: Record<string, AnalogInputsHistoryEntry[]>;
autoLoad: boolean;
} }
const initialState: InputHistoryState = { const initialState: InputHistoryState = {
@@ -25,6 +26,7 @@ const initialState: InputHistoryState = {
isLoading: false, isLoading: false,
error: null, error: null,
data: {}, data: {},
autoLoad: false,
}; };
export const analogInputsHistorySlice = createSlice({ export const analogInputsHistorySlice = createSlice({
@@ -34,6 +36,9 @@ export const analogInputsHistorySlice = createSlice({
setSelectedId: (state, action: PayloadAction<number | null>) => { setSelectedId: (state, action: PayloadAction<number | null>) => {
state.selectedId = action.payload; state.selectedId = action.payload;
}, },
setAutoLoad: (state, action: PayloadAction<boolean>) => {
state.autoLoad = action.payload; // ✅ AutoLoad steuern
},
setZeitraum: (state, action: PayloadAction<string>) => { setZeitraum: (state, action: PayloadAction<string>) => {
state.zeitraum = action.payload; state.zeitraum = action.payload;
}, },
@@ -64,6 +69,7 @@ export const analogInputsHistorySlice = createSlice({
export const { export const {
setSelectedId, setSelectedId,
setAutoLoad,
setZeitraum, setZeitraum,
setVonDatum, setVonDatum,
setBisDatum, setBisDatum,