fix: Vergleich und Speicherung von Änderungen im KUE-Modul korrigiert

- Originalwerte werden jetzt direkt aus window.win_kueXYZ geladen
- Vergleiche in handleSave.ts angepasst für stabile Zahl/String-Auswertung
- Fehlerhafte Meldung „Keine Änderungen vorgenommen“ behoben
- Nur geänderte Werte werden per GET-API gesendet
This commit is contained in:
ISA
2025-04-22 11:59:44 +02:00
parent d0568dc576
commit cfbc56206c
11 changed files with 519 additions and 392 deletions

View File

@@ -46,7 +46,11 @@ var win_kueIso = [
10.5, 10.0, 200.0, 200.0, 200.0, 200.0, 10.5, 10.0, 200.0, 200.0, 200.0, 200.0,
]; ];
//Grenzwert (MOhm) für Isolationswiderstand //Grenzwert (MOhm) für Isolationswiderstand
var win_kueLimit1 = [ 8.9, "9.9", 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, ]; var win_kueLimit1 = [
8.9, 9.9, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0,
10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0,
10.0, 10.0, 10.0, 10.0, 10.0, 10.0,
];
/* /*
Verzögerung/Filterzeit 420 Sekunden Standardeinstellung Verzögerung/Filterzeit 420 Sekunden Standardeinstellung
@@ -57,7 +61,11 @@ Wenn der Widerstand innerhalb dieser 420 Sekunden wieder über den Grenzwert ste
die Filterzeit startet beim nächsten Unterschreiten des Grenzwerts neu. Die Filterzeit verhindert also, dass die Filterzeit startet beim nächsten Unterschreiten des Grenzwerts neu. Die Filterzeit verhindert also, dass
kurzfristige Schwankungen oder Störungen fälschlicherweise als Fehler gemeldet werden. kurzfristige Schwankungen oder Störungen fälschlicherweise als Fehler gemeldet werden.
*/ */
var win_kueDelay1 = [ 10, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, ]; var win_kueDelay1 = [
10, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420, 420,
420,
];
//--------------------------------------------------- //---------------------------------------------------
//Schleifenwiderstand in Display (resDisplay) Einheit: KOhm //Schleifenwiderstand in Display (resDisplay) Einheit: KOhm
var win_kueResidence = [ var win_kueResidence = [
@@ -66,11 +74,18 @@ var win_kueResidence = [
0.615, 0.494, 1.217, 65.0, 65.0, 65.0, 65.0, 0.615, 0.494, 1.217, 65.0, 65.0, 65.0, 65.0,
]; ];
//Schleifenmessung Unterer Grenzwert (KOhm) //Schleifenmessung Unterer Grenzwert (KOhm)
var win_kueLimit2Low = [ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, ]; var win_kueLimit2Low = [
var win_kueLimit2High = [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]; 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1,
0.1, 0.1,
];
var win_kueLimit2High = [ "undefined", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ];;
//Schleifenintervall (h) für Schleifenmessung //Schleifenintervall (h) für Schleifenmessung
var win_kueLoopInterval = [ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, ]; var win_kueLoopInterval = [
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6,
];
//--------------------------------------------------- //---------------------------------------------------
//KÜ Modul Version soll /100 und davor V angezeigt werden z.B. 4.19V //KÜ Modul Version soll /100 und davor V angezeigt werden z.B. 4.19V
var win_kueVersion = [ var win_kueVersion = [
@@ -80,7 +95,7 @@ var win_kueVersion = [
]; ];
//Modulname in Komponente und auf der Anzeige //Modulname in Komponente und auf der Anzeige
var win_kueID = [ "FTZ_2", "B23", "Kabel 3", "Kabel 4", "Kabel 5", "Kabel 6", "FTZ4562", "Kabel 8", "12344", "Kabel 10", "Kabel 11", "Kabel 12", "Kabel 13", "Kabel 14", "Kabel 15", "H56-77", "Kabel 17", "Kabel 18", "Kabel 19", "Kabel 20", "Kabel 21", "Kabel 22", "Kabel 23", "Kabel 24", "Kabel 25", "Kabel 26", "Kabel 27", "Kabel 28", "Kabel 29", "Kabel 30", "Kabel 31", "Kabel 32", ]; var win_kueID = [ "FTZ_4", "B23", "Kabel 3", "Kabel 4", "Kabel 5", "Kabel 6", "FTZ4562", "Kabel 8", "12344", "Kabel 10", "Kabel 11", "Kabel 12", "Kabel 13", "Kabel 14", "Kabel 15", "H56-77", "Kabel 17", "Kabel 18", "Kabel 19", "Kabel 20", "Kabel 21", "Kabel 22", "Kabel 23", "Kabel 24", "Kabel 25", "Kabel 26", "Kabel 27", "Kabel 28", "Kabel 29", "Kabel 30", "Kabel 31", "Kabel 32", ];;;
//--------------------------------------------------- //---------------------------------------------------

View File

@@ -1,7 +1,7 @@
"use client"; // components/modules/kue705FO/Kue705FO.tsx "use client"; // components/modules/kue705FO/Kue705FO.tsx
import React, { useState, useEffect, useRef, useMemo } from "react"; import React, { useState, useEffect, useRef, useMemo } from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import KueModal from "./KueModal"; import KueModal from "./modals/KueModal";
import "bootstrap-icons/font/bootstrap-icons.css"; // Import Bootstrap Icons import "bootstrap-icons/font/bootstrap-icons.css"; // Import Bootstrap Icons
import { Kue705FOProps } from "../../../../types/components/Kue705FOProps"; import { Kue705FOProps } from "../../../../types/components/Kue705FOProps";
import ChartSwitcher from "./Charts/ChartSwitcher"; import ChartSwitcher from "./Charts/ChartSwitcher";

View File

@@ -1,304 +0,0 @@
"use client"; // components/modales/KueModal.jsx
import ReactModal from "react-modal";
import { useEffect, useState } from "react";
import { useSelector, useDispatch } from "react-redux";
import type { RootState } from "../../../../redux/store";
import "bootstrap-icons/font/bootstrap-icons.css";
import handleSave, { OriginalValues } from "./handlers/handleSave";
import handleDisplayEinschalten from "./handlers/handleDisplayEinschalten";
import firmwareUpdate from "./handlers/firmwareUpdate";
import decodeToken from "../../../../utils/decodeToken";
import { setKueData } from "../../../../redux/slices/kueDataSlice";
interface KueModalProps {
showModal: boolean;
onClose: () => void;
slot: number;
onModulNameChange: (id: string) => void;
}
function KueModal({
showModal,
onClose,
slot,
onModulNameChange,
}: KueModalProps): JSX.Element {
const dispatch = useDispatch();
const isAdminLoggedIn = useSelector(
(state: any) => state.authSlice.isAdminLoggedIn
);
const [isAdmin, setIsAdmin] = useState(false);
const {
kueID,
kueLimit1,
kueDelay1,
kueLimit2Low,
kueLimit2High,
kueLoopInterval,
} = useSelector((state: RootState) => state.kueDataSlice);
const [bezeichnungen, setBezeichnungen] = useState(Array(32).fill(""));
const [originalValues, setOriginalValues] = useState<OriginalValues>({
kueID: Array(32).fill(""),
kueBezeichnungen: Array(32).fill(""),
isolationsgrenzwerte: Array(32).fill(10.0),
verzoegerung: Array(32).fill(1.0),
untereSchleifenGrenzwerte: Array(32).fill(0.1),
obereSchleifenGrenzwerte: Array(32).fill(1.0),
schleifenintervall: Array(32).fill(24),
});
useEffect(() => {
if (showModal) {
setBezeichnungen(
kueID
? kueID.map((name: string) => name.trim() || "---")
: bezeichnungen
);
setOriginalValues({
kueID: [...kueID],
kueBezeichnungen: [...bezeichnungen],
isolationsgrenzwerte: [...kueLimit1],
verzoegerung: [...kueDelay1],
untereSchleifenGrenzwerte: [...kueLimit2Low],
obereSchleifenGrenzwerte: [...kueLimit2High],
schleifenintervall: [...kueLoopInterval],
});
}
}, [
showModal,
kueID,
kueLimit1,
kueDelay1,
kueLimit2Low,
kueLimit2High,
kueLoopInterval,
]);
useEffect(() => {
const token = sessionStorage.getItem("token");
if (token) {
const decoded = decodeToken(token);
setIsAdmin(decoded?.role?.toLowerCase() === "admin");
}
}, [showModal]);
const handleSaveWrapper = () => {
handleSave({
ids: [...kueID],
bezeichnungen,
isolationsgrenzwerte: [...kueLimit1],
verzoegerung: [...kueDelay1],
untereSchleifenGrenzwerte: [...kueLimit2Low],
obereSchleifenGrenzwerte: [...kueLimit2High],
schleifenintervall: [...kueLoopInterval],
originalValues,
slot,
dispatch,
onModulNameChange,
onClose,
});
};
// Korrekte updateArray Methode ohne Thunk
const updateArray = (
key: keyof RootState["kueDataSlice"],
array: number[],
value: number
) => {
const updatedArray = [...array];
updatedArray[slot] = value;
dispatch(setKueData({ [key]: updatedArray }));
};
const handleDisplayEinschaltenWrapper = () => {
handleDisplayEinschalten(slot);
};
return (
<ReactModal
isOpen={showModal}
onRequestClose={onClose}
ariaHideApp={false}
style={{
overlay: {
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 100,
},
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
width: "90%",
maxWidth: "800px",
padding: "10px",
borderRadius: "8px",
border: "none",
position: "relative", // wichtig
},
}}
>
<div className="relative bg-littwin-blue text-white p-1 rounded-t-lg">
<h2 className="text-sm font-bold">KUE Einstellung - Slot {slot + 1}</h2>
<button
onClick={onClose}
style={{
position: "absolute",
top: "0rem",
right: "0.5rem",
background: "transparent",
border: "none",
fontSize: "1.5rem",
color: "#000",
cursor: "pointer",
zIndex: 10,
}}
>
<i className="bi bi-x-circle-fill"></i>
</button>
</div>
<div className="p-2 mb-4 text-black">
<label className="font-bold">Kabelbezeichnung:</label>
<input
type="text"
className="border rounded p-1 w-full text-sm"
value={kueID[slot] || ""}
onChange={(e) => {
const newIds = [...kueID];
newIds[slot] = e.target.value;
dispatch(setKueData({ kueID: newIds }));
}}
/>
</div>
<div className="p-2 text-black">
<h3 className="font-bold text-center mb-4">Isolationsmessung</h3>
<table className="w-full text-left border-collapse mb-4">
<thead className="bg-gray-100">
<tr>
<th className="p-2 border text-sm text-center">
Grenzwert (MOhm)
</th>
<th className="p-2 border text-sm text-center">
Verzögerung (sek)
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="p-2 border text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1 text-sm"
value={kueLimit1[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLimit1",
kueLimit1,
parseFloat(e.target.value)
)
}
/>
</td>
<td className="p-2 border text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1 text-sm"
value={kueDelay1[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueDelay1",
kueDelay1,
parseFloat(e.target.value)
)
}
/>
</td>
</tr>
</tbody>
</table>
<h3 className="font-bold text-center mb-4">Schleifenmessung</h3>
<table className="w-full text-left border-collapse">
<thead className="bg-gray-100">
<tr>
<th className="p-2 border text-sm text-center">
Grenzwert (kOhm)
</th>
<th className="p-2 border text-sm text-center">
Schleifenintervall (h)
</th>
</tr>
</thead>
<tbody>
<tr>
<td className="p-2 border text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1 text-sm"
value={kueLimit2Low[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLimit2Low",
kueLimit2Low,
parseFloat(e.target.value)
)
}
/>
</td>
<td className="p-2 border text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1 text-sm"
value={kueLoopInterval[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLoopInterval",
kueLoopInterval,
parseFloat(e.target.value)
)
}
/>
</td>
</tr>
</tbody>
</table>
</div>
<div className="flex justify-end bg-gray-100 p-4 rounded-b-lg">
{isAdminLoggedIn && (
<button
onClick={() => firmwareUpdate(slot)}
className="bg-littwin-blue text-white p-2 rounded flex items-center mr-2"
>
Firmware Update
</button>
)}
<button
onClick={handleDisplayEinschaltenWrapper}
className="bg-littwin-blue text-white p-2 rounded flex items-center mr-2"
>
<span className="mr-2">📺</span> Display einschalten
</button>
<button
onClick={handleSaveWrapper}
className="bg-littwin-blue text-white p-2 rounded flex items-center"
>
<span className="mr-2">💾</span> Speichern
</button>
</div>
</ReactModal>
);
}
export default KueModal;

View File

@@ -3,7 +3,6 @@ import { setKueData } from "../../../../../redux/slices/kueDataSlice";
export interface OriginalValues { export interface OriginalValues {
kueID: string[]; kueID: string[];
kueBezeichnungen: string[];
isolationsgrenzwerte: number[]; isolationsgrenzwerte: number[];
verzoegerung: number[]; verzoegerung: number[];
untereSchleifenGrenzwerte: number[]; untereSchleifenGrenzwerte: number[];
@@ -13,7 +12,6 @@ export interface OriginalValues {
interface HandleSaveParams { interface HandleSaveParams {
ids: string[]; ids: string[];
bezeichnungen: string[];
isolationsgrenzwerte: number[]; isolationsgrenzwerte: number[];
verzoegerung: number[]; verzoegerung: number[];
untereSchleifenGrenzwerte: number[]; untereSchleifenGrenzwerte: number[];
@@ -26,9 +24,17 @@ interface HandleSaveParams {
onClose: () => void; onClose: () => void;
} }
const isDifferent = (a: any, b: any): boolean => {
const aNum = Number(a);
const bNum = Number(b);
if (!isNaN(aNum) && !isNaN(bNum)) {
return Math.abs(aNum - bNum) > 0.0001;
}
return String(a).trim() !== String(b).trim();
};
const handleSave = async ({ const handleSave = async ({
ids, ids,
bezeichnungen,
isolationsgrenzwerte, isolationsgrenzwerte,
verzoegerung, verzoegerung,
untereSchleifenGrenzwerte, untereSchleifenGrenzwerte,
@@ -40,88 +46,73 @@ const handleSave = async ({
onModulNameChange, onModulNameChange,
onClose, onClose,
}: HandleSaveParams): Promise<void> => { }: HandleSaveParams): Promise<void> => {
const changes: Partial<{ const changesForFile: Record<string, any> = {};
KID: string;
KIA: string;
KL_: number;
KD_: number;
KR_: number;
KRO_: number;
KRI: number;
}> = {};
if (ids[slot] !== originalValues.kueID[slot]) { if (isDifferent(ids[slot], originalValues.kueID[slot])) {
changes.KID = ids[slot]; changesForFile.win_kueID = ids[slot];
}
if (bezeichnungen[slot] !== originalValues.kueBezeichnungen[slot]) {
changes.KIA = bezeichnungen[slot];
} }
if ( if (
isolationsgrenzwerte[slot] !== originalValues.isolationsgrenzwerte[slot] isDifferent(
isolationsgrenzwerte[slot],
originalValues.isolationsgrenzwerte[slot]
)
) { ) {
changes.KL_ = isolationsgrenzwerte[slot]; changesForFile.win_kueLimit1 = isolationsgrenzwerte[slot];
} }
if (verzoegerung[slot] !== originalValues.verzoegerung[slot]) { if (isDifferent(verzoegerung[slot], originalValues.verzoegerung[slot])) {
changes.KD_ = verzoegerung[slot]; changesForFile.win_kueDelay1 = verzoegerung[slot];
} }
if ( if (
untereSchleifenGrenzwerte[slot] !== isDifferent(
originalValues.untereSchleifenGrenzwerte[slot] untereSchleifenGrenzwerte[slot],
originalValues.untereSchleifenGrenzwerte[slot]
)
) { ) {
changes.KR_ = untereSchleifenGrenzwerte[slot]; changesForFile.win_kueLimit2Low = untereSchleifenGrenzwerte[slot];
} }
if ( if (
obereSchleifenGrenzwerte[slot] !== isDifferent(
originalValues.obereSchleifenGrenzwerte[slot] obereSchleifenGrenzwerte[slot],
originalValues.obereSchleifenGrenzwerte[slot]
)
) { ) {
changes.KRO_ = obereSchleifenGrenzwerte[slot]; changesForFile.win_kueLimit2High = obereSchleifenGrenzwerte[slot];
} }
if (schleifenintervall[slot] !== originalValues.schleifenintervall[slot]) { if (
changes.KRI = schleifenintervall[slot]; isDifferent(
schleifenintervall[slot],
originalValues.schleifenintervall[slot]
)
) {
changesForFile.win_kueLoopInterval = schleifenintervall[slot];
} }
if (Object.keys(changes).length > 0) { if (Object.keys(changesForFile).length > 0) {
const isDev = window.location.hostname === "localhost"; const isDev = window.location.hostname === "localhost";
if (isDev) { if (isDev) {
await fetch("/api/cpl/updateKueDataAPIHandler", { for (const [key, value] of Object.entries(changesForFile)) {
method: "POST", const params = new URLSearchParams({
headers: { "Content-Type": "application/json" }, key,
body: JSON.stringify({ value: String(value),
slot, slot: String(slot),
changes: { });
win_kueID: ids[slot], const response = await fetch(
win_kueLimit1: isolationsgrenzwerte[slot], `/api/cpl/updateKueDataAPIHandler?${params.toString()}`
win_kueDelay1: verzoegerung[slot], );
win_kueLimit2Low: untereSchleifenGrenzwerte[slot], if (!response.ok) {
win_kueLimit2High: obereSchleifenGrenzwerte[slot], alert(`❌ Fehler beim Schreiben der Datei (${key})`);
win_kueLoopInterval: schleifenintervall[slot], return;
},
}),
});
} else {
let url = `/cpl?/kabelueberwachung.html&slot=${slot}`;
Object.entries(changes).forEach(([paramKey, paramValue]) => {
if (paramValue !== undefined) {
url += `&${paramKey}${slot}=${encodeURIComponent(paramValue)}`;
} }
});
const response = await fetch(url, { method: "GET" });
if (!response.ok) {
alert("Fehler beim Speichern der Daten!");
return;
} }
} }
alert("Daten erfolgreich gespeichert!"); alert("Daten erfolgreich gespeichert!");
onModulNameChange(ids[slot]); onModulNameChange(ids[slot]);
dispatch( dispatch(
setKueData({ setKueData({
kueID: [...ids], kueID: [...ids],
kueBezeichnungen: [...bezeichnungen],
isolationsgrenzwerte: [...isolationsgrenzwerte], isolationsgrenzwerte: [...isolationsgrenzwerte],
verzoegerung: [...verzoegerung], verzoegerung: [...verzoegerung],
untereSchleifenGrenzwerte: [...untereSchleifenGrenzwerte], untereSchleifenGrenzwerte: [...untereSchleifenGrenzwerte],
@@ -130,7 +121,7 @@ const handleSave = async ({
}) })
); );
} else { } else {
alert("Keine Änderungen vorgenommen."); alert(" Keine Änderungen vorgenommen.");
} }
onClose(); onClose();

View File

@@ -0,0 +1,9 @@
import React from "react";
interface Props {
slot: number;
}
export default function Knotenpunkte({ slot }: Props) {
return <div>Knotenpunkte Slot {slot + 1}</div>;
}

View File

@@ -0,0 +1,201 @@
"use client";
import { useDispatch, useSelector } from "react-redux";
import type { RootState } from "../../../../../redux/store";
import { setKueData } from "../../../../../redux/slices/kueDataSlice";
import handleSave, { OriginalValues } from "../handlers/handleSave";
import handleDisplayEinschalten from "../handlers/handleDisplayEinschalten";
import firmwareUpdate from "../handlers/firmwareUpdate";
interface Props {
slot: number;
onClose?: () => void;
onModulNameChange?: (id: string) => void;
}
export default function KueEinstellung({
slot,
onClose = () => {},
onModulNameChange = () => {},
}: Props) {
const dispatch = useDispatch();
const {
kueID,
kueLimit1,
kueDelay1,
kueLimit2Low,
kueLoopInterval,
kueLimit2High,
} = useSelector((state: RootState) => state.kueDataSlice);
const isAdminLoggedIn = useSelector(
(state: any) => state.authSlice.isAdminLoggedIn
);
const handleSaveWrapper = () => {
const originalValues: OriginalValues = {
kueID: [...(window.win_kueID ?? [])],
isolationsgrenzwerte: [...(window.win_kueLimit1 ?? [])],
verzoegerung: [...(window.win_kueDelay1 ?? [])],
untereSchleifenGrenzwerte: [...(window.win_kueLimit2Low ?? [])],
obereSchleifenGrenzwerte: [...(window.win_kueLimit2High ?? [])],
schleifenintervall: [...(window.win_kueLoopInterval ?? [])],
};
handleSave({
ids: [...kueID],
isolationsgrenzwerte: [...kueLimit1],
verzoegerung: [...kueDelay1],
untereSchleifenGrenzwerte: [...kueLimit2Low],
obereSchleifenGrenzwerte: [...kueLimit2High],
schleifenintervall: [...kueLoopInterval],
originalValues,
slot,
dispatch,
onModulNameChange,
onClose,
});
};
const updateArray = (
key: keyof RootState["kueDataSlice"],
array: number[],
value: number
) => {
const updated = [...array];
updated[slot] = value;
dispatch(setKueData({ [key]: updated }));
};
return (
<div className="text-black space-y-6">
<div>
<label className="font-bold">Kabelbezeichnung:</label>
<input
type="text"
className="w-full border rounded p-1 text-sm"
value={kueID[slot] || ""}
onChange={(e) => {
const newIds = [...kueID];
newIds[slot] = e.target.value;
dispatch(setKueData({ kueID: newIds }));
}}
/>
</div>
<div>
<h3 className="font-bold text-center mb-2">Isolationsmessung</h3>
<table className="w-full border-collapse text-sm">
<thead className="bg-gray-100">
<tr>
<th className="border p-2 text-center">Grenzwert (MOhm)</th>
<th className="border p-2 text-center">Verzögerung (sek)</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border p-2 text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1"
value={kueLimit1[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLimit1",
kueLimit1,
parseFloat(e.target.value)
)
}
/>
</td>
<td className="border p-2 text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1"
value={kueDelay1[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueDelay1",
kueDelay1,
parseFloat(e.target.value)
)
}
/>
</td>
</tr>
</tbody>
</table>
</div>
<div>
<h3 className="font-bold text-center mb-2">Schleifenmessung</h3>
<table className="w-full border-collapse text-sm">
<thead className="bg-gray-100">
<tr>
<th className="border p-2 text-center">Grenzwert (kOhm)</th>
<th className="border p-2 text-center">Schleifenintervall (h)</th>
</tr>
</thead>
<tbody>
<tr>
<td className="border p-2 text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1"
value={kueLimit2Low[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLimit2Low",
kueLimit2Low,
parseFloat(e.target.value)
)
}
/>
</td>
<td className="border p-2 text-center">
<input
type="number"
step="0.1"
className="w-[6rem] border rounded p-1"
value={kueLoopInterval[slot] ?? ""}
onChange={(e) =>
updateArray(
"kueLoopInterval",
kueLoopInterval,
parseFloat(e.target.value)
)
}
/>
</td>
</tr>
</tbody>
</table>
</div>
<div className="flex justify-end gap-2 bg-gray-100 p-3 rounded">
{isAdminLoggedIn && (
<button
onClick={() => firmwareUpdate(slot)}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
<span className="mr-2">🔧</span> Firmware Update
</button>
)}
<button
onClick={() => handleDisplayEinschalten(slot)}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
<span className="mr-2">📺</span> Display einschalten
</button>
<button
onClick={handleSaveWrapper}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
<span className="mr-2">💾</span> Speichern
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,108 @@
"use client";
import { useState, useEffect } from "react";
import ReactModal from "react-modal";
import KueEinstellung from "./KueEinstellung";
import TdrEinstellung from "./TdrEinstellung";
import Knotenpunkte from "./Knotenpunkte";
interface KueModalProps {
showModal: boolean;
onClose: () => void;
slot: number;
onModulNameChange: (id: string) => void; // NEU!
}
export default function KueModal({ showModal, onClose, slot }: KueModalProps) {
const [activeTab, setActiveTab] = useState<"kue" | "tdr" | "knoten">("kue");
useEffect(() => {
if (showModal) {
setActiveTab("kue");
}
}, [showModal]);
return (
<ReactModal
isOpen={showModal}
onRequestClose={onClose}
ariaHideApp={false}
style={{
overlay: {
backgroundColor: "rgba(0, 0, 0, 0.5)",
zIndex: 100,
},
content: {
top: "50%",
left: "50%",
right: "auto",
bottom: "auto",
transform: "translate(-50%, -50%)",
width: "90%",
maxWidth: "850px",
padding: "0px",
border: "none",
borderRadius: "8px",
position: "relative",
},
}}
>
<div className="bg-littwin-blue text-white p-2 flex justify-between items-center rounded-t-md">
<h2 className="text-sm font-bold">Einstellungen Slot {slot + 1}</h2>
<button
onClick={onClose}
className="text-white text-xl hover:text-gray-200"
>
<i className="bi bi-x-circle-fill"></i>
</button>
</div>
<div className="flex justify-center bg-gray-100 space-x-2 p-2">
<button
onClick={() => setActiveTab("kue")}
className={`px-4 py-1 rounded-t font-bold text-sm ${
activeTab === "kue"
? "bg-white text-blue-600"
: "text-gray-500 hover:text-black"
}`}
>
KUE Einstellung
</button>
<button
onClick={() => setActiveTab("tdr")}
className={`px-4 py-1 rounded-t font-bold text-sm ${
activeTab === "tdr"
? "bg-white text-blue-600"
: "text-gray-500 hover:text-black"
}`}
>
TDR Einstellung
</button>
<button
onClick={() => setActiveTab("knoten")}
className={`px-4 py-1 rounded-t font-bold text-sm ${
activeTab === "knoten"
? "bg-white text-blue-600"
: "text-gray-500 hover:text-black"
}`}
>
Knotenpunkte
</button>
</div>
<div className="p-4 bg-white rounded-b-md max-h-[75vh] overflow-y-auto">
{activeTab === "kue" && (
<KueEinstellung
slot={slot}
onModulNameChange={(id) => {
console.log("Modulname geändert:", id);
}}
onClose={onClose}
/>
)}
{activeTab === "tdr" && <TdrEinstellung slot={slot} />}
{activeTab === "knoten" && <Knotenpunkte slot={slot} />}
</div>
</ReactModal>
);
}

View File

@@ -0,0 +1,9 @@
import React from "react";
interface Props {
slot: number;
}
export default function TdrEinstellung({ slot }: Props) {
return <div>TdrEinstellung Slot {slot + 1}</div>;
}

View File

@@ -6,5 +6,5 @@
2: Patch oder Hotfix (Bugfixes oder kleine Änderungen). 2: Patch oder Hotfix (Bugfixes oder kleine Änderungen).
*/ */
const webVersion = "1.6.246"; const webVersion = "1.6.247";
export default webVersion; export default webVersion;

View File

@@ -7,14 +7,16 @@ export default async function handler(
req: NextApiRequest, req: NextApiRequest,
res: NextApiResponse res: NextApiResponse
) { ) {
if (req.method !== "POST") { if (req.method !== "GET") {
return res.status(405).json({ error: "Method not allowed" }); return res.status(405).json({ error: "Only GET allowed" });
} }
const { slot, changes } = req.body; const slot = parseInt(req.query.slot as string);
const key = req.query.key as string;
const value = req.query.value as string;
if (slot === undefined || !changes || typeof changes !== "object") { if (isNaN(slot) || !key || value === undefined) {
return res.status(400).json({ error: "Missing slot or changes" }); return res.status(400).json({ error: "Missing slot, key or value" });
} }
const filePath = path.join( const filePath = path.join(
@@ -27,26 +29,47 @@ export default async function handler(
try { try {
let fileContent = await fs.readFile(filePath, "utf-8"); let fileContent = await fs.readFile(filePath, "utf-8");
for (const [varName, value] of Object.entries(changes)) { // Robuste Regex: matcht auch bei Zeilenumbrüchen oder Leerzeichen
const regex = new RegExp( const regex = new RegExp(
`(var\\s+${varName}\\s*=\\s*\\[)([^\\]]*)(\\])`, `(var\\s+${key}\\s*=\\s*\\[)([\\s\\S]*?)(\\])`,
"m" "gm"
); );
const match = fileContent.match(regex); const match = fileContent.match(regex);
if (match) {
const values = match[2].split(",").map((v) => v.trim()); console.log("🔍 Suche nach Variable:", key);
values[slot] = JSON.stringify(value); // korrektes Format: z.B. `"FTZ_2"` oder `10.5` if (!match) {
const updatedArray = `${match[1]} ${values.join(", ")} ${match[3]}`; console.warn(`⚠️ Variable '${key}' nicht gefunden in Datei.`);
fileContent = fileContent.replace(regex, updatedArray); return res.status(404).json({ error: `Variable '${key}' not found.` });
}
} }
const arrayPart = match[0].match(/\[(.*)\]/s)?.[1] ?? "";
const values = arrayPart
.split(",")
.map((v) => v.trim())
.filter((v) => v.length > 0 || v === "");
const parsedValue = isNaN(Number(value)) ? JSON.stringify(value) : value;
if (slot >= values.length) {
return res
.status(400)
.json({ error: `Slot ${slot} ist außerhalb des gültigen Bereichs.` });
}
values[slot] = parsedValue;
const updatedArray = `var ${key} = [ ${values.join(", ")} ];`;
fileContent = fileContent.replace(regex, updatedArray);
await fs.writeFile(filePath, fileContent, "utf-8"); await fs.writeFile(filePath, fileContent, "utf-8");
res.status(200).json({ success: true }); console.log(`${key}[${slot}] erfolgreich geändert auf`, parsedValue);
return res
.status(200)
.json({ success: true, key, slot, newValue: parsedValue });
} catch (error) { } catch (error) {
console.error("Fehler beim Schreiben der Datei:", error); console.error("Fehler beim Schreiben der Datei:", error);
res.status(500).json({ error: "Failed to write file" }); return res.status(500).json({ error: "Failed to write mock file" });
} }
} }

View File

@@ -0,0 +1,75 @@
// /pages/api/cpl/updateMockViaGet.ts
import { NextApiRequest, NextApiResponse } from "next";
import path from "path";
import fs from "fs/promises";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Only GET allowed" });
}
const slot = parseInt(req.query.slot as string);
const key = req.query.key as string;
const value = req.query.value as string;
if (isNaN(slot) || !key || value === undefined) {
return res.status(400).json({ error: "Missing slot, key or value" });
}
const filePath = path.join(
process.cwd(),
"apiMockData",
"SERVICE",
"kabelueberwachungMockData.js"
);
try {
let fileContent = await fs.readFile(filePath, "utf-8");
// Robuste Regex: matcht auch bei Zeilenumbrüchen oder Leerzeichen
const regex = new RegExp(
`(var\\s+${key}\\s*=\\s*\\[)([\\s\\S]*?)(\\])`,
"gm"
);
const match = fileContent.match(regex);
console.log("🔍 Suche nach Variable:", key);
if (!match) {
console.warn(`⚠️ Variable '${key}' nicht gefunden in Datei.`);
return res.status(404).json({ error: `Variable '${key}' not found.` });
}
const arrayPart = match[0].match(/\[(.*)\]/s)?.[1] ?? "";
const values = arrayPart
.split(",")
.map((v) => v.trim())
.filter((v) => v.length > 0 || v === "");
const parsedValue = isNaN(Number(value)) ? JSON.stringify(value) : value;
if (slot >= values.length) {
return res
.status(400)
.json({ error: `Slot ${slot} ist außerhalb des gültigen Bereichs.` });
}
values[slot] = parsedValue;
const updatedArray = `var ${key} = [ ${values.join(", ")} ];`;
fileContent = fileContent.replace(regex, updatedArray);
await fs.writeFile(filePath, fileContent, "utf-8");
console.log(`${key}[${slot}] erfolgreich geändert auf`, parsedValue);
return res
.status(200)
.json({ success: true, key, slot, newValue: parsedValue });
} catch (error) {
console.error("❌ Fehler beim Schreiben der Datei:", error);
return res.status(500).json({ error: "Failed to write mock file" });
}
}