Compare commits

10 Commits

Author SHA1 Message Date
Ismail Ali
937e7b67e9 fix: Firmware-Update läuft nun exakt 5 Minuten bis 100 % Fortschritt
- Fehler behoben, bei dem das Firmware-Update nach wenigen Sekunden vorzeitig beendet wurde
- Fortschrittsanzeige über Redux-Slice `firmwareProgressSlice` korrekt umgesetzt
- Thunk `startFirmwareUpdateThunk` korrekt eingebunden und verwendet
- UI zeigt stabile 5-minütige Progressbar wie erwartet
2025-07-02 22:01:47 +02:00
Ismail Ali
b23d939481 fix: Firmware-Update läuft nun exakt 5 Minuten bis 100 % Fortschritt
- Fehler behoben, bei dem das Firmware-Update nach wenigen Sekunden vorzeitig beendet wurde
- Fortschrittsanzeige über Redux-Slice `firmwareProgressSlice` korrekt umgesetzt
- Thunk `startFirmwareUpdateThunk` korrekt eingebunden und verwendet
- UI zeigt stabile 5-minütige Progressbar wie erwartet
2025-07-02 22:01:17 +02:00
ISA
e9e929f577 fix: Toast-Benachrichtigungen wiederhergestellt durch Einbindung von ToastContainer
- <ToastContainer /> in _app.tsx hinzugefügt
- react-toastify funktioniert jetzt wie vorgesehen (z. B. Firmware-Update Feedback)
- autoClose-Zeit für bessere Sichtbarkeit ggf. angepasst
2025-07-02 14:24:21 +02:00
ISA
f50bff4819 fix: ConfirmModal-Zustand in Redux ausgelagert zur Stabilisierung
- Neuen confirmModalSlice erstellt für globale Steuerung des Bestätigungsdialogs
- Zustand wird nun nicht mehr durch Re-Renders oder Komponentenneuaufbau zurückgesetzt
- ConfirmModal in KueEinstellung.tsx vollständig an Redux angebunden
- Flackern und automatisches Schließen nach 10–15 Sekunden dauerhaft behoben
2025-07-02 14:16:08 +02:00
ISA
b3c5580538 feat: Auth-Status bei App-Start aus localStorage laden und in Redux speichern
- fetchAuthService erstellt zum Auslesen von isAdminLoggedIn aus localStorage
- getAuthThunks Thunk implementiert zur Initialisierung von authSlice
- authSlice erweitert um setIsAdminLoggedIn Reducer
- dispatch(getAuthThunks()) in _app.tsx integriert für automatische Initialisierung bei App-Start
- Flackern und falscher Admin-Status nach Reload dauerhaft behoben
2025-07-02 13:55:27 +02:00
ISA
1ec2c5cc14 refactor: Admin-Status direkt aus Redux ausgelesen und Props entfernt
- isAdminLoggedIn wird jetzt direkt aus authSlice im Redux-Store gelesen
- useAdminAuth und Prop-Weitergabe entfernt
- Flackern des Firmware-Buttons dauerhaft behoben
- Codestruktur vereinfacht und stabilisiert
2025-07-02 12:35:52 +02:00
ISA
a7d1e1e8df refactor: Admin-Status direkt aus Redux ausgelesen und Props entfernt
- isAdminLoggedIn wird jetzt direkt aus authSlice im Redux-Store gelesen
- useAdminAuth und Prop-Weitergabe entfernt
- Flackern des Firmware-Buttons dauerhaft behoben
- Codestruktur vereinfacht und stabilisiert
2025-07-02 12:35:15 +02:00
ISA
e46e23fada fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
2025-07-02 12:03:41 +02:00
ISA
a9f6484fb0 fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
2025-07-02 12:03:02 +02:00
ISA
1dfa1cc1ba feat: Firmwareupdate für alle KÜ-Module mit Fortschrittsanzeige und Abschlussmeldung
- ProgressModal-Komponente implementiert, die während des Updates angezeigt wird
- Firmwareupdate dauert 5 Minuten (Mock-Simulation)
- Nach Abschluss erscheint automatisch ein Toast-Hinweis
- Verbesserte Benutzerführung durch blockierendes Modal während Update
- Logging in kueFirmwareUpdateLog.json integriert (Mock)
2025-07-01 10:08:33 +02:00
24 changed files with 697 additions and 115 deletions

View File

@@ -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.512
NEXT_PUBLIC_APP_VERSION=1.6.526
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_USE_CGI=true
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.512
NEXT_PUBLIC_APP_VERSION=1.6.526
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,135 @@
## [1.6.526] 2025-07-02
- fix: Firmware-Update läuft nun exakt 5 Minuten bis 100% Fortschritt
- Fehler behoben, bei dem das Firmware-Update nach wenigen Sekunden vorzeitig beendet wurde
- Fortschrittsanzeige über Redux-Slice `firmwareProgressSlice` korrekt umgesetzt
- Thunk `startFirmwareUpdateThunk` korrekt eingebunden und verwendet
- UI zeigt stabile 5-minütige Progressbar wie erwartet
---
## [1.6.525] 2025-07-02
- fix: Toast-Benachrichtigungen wiederhergestellt durch Einbindung von ToastContainer
- <ToastContainer /> in _app.tsx hinzugefügt
- react-toastify funktioniert jetzt wie vorgesehen (z.B. Firmware-Update Feedback)
- autoClose-Zeit für bessere Sichtbarkeit ggf. angepasst
---
## [1.6.524] 2025-07-02
- fix: Toast-Benachrichtigungen wiederhergestellt durch Einbindung von ToastContainer
- <ToastContainer /> in _app.tsx hinzugefügt
- react-toastify funktioniert jetzt wie vorgesehen (z.B. Firmware-Update Feedback)
- autoClose-Zeit für bessere Sichtbarkeit ggf. angepasst
---
## [1.6.523] 2025-07-02
- fix: ConfirmModal-Zustand in Redux ausgelagert zur Stabilisierung
- Neuen confirmModalSlice erstellt für globale Steuerung des Bestätigungsdialogs
- Zustand wird nun nicht mehr durch Re-Renders oder Komponentenneuaufbau zurückgesetzt
- ConfirmModal in KueEinstellung.tsx vollständig an Redux angebunden
- Flackern und automatisches Schließen nach 1015 Sekunden dauerhaft behoben
---
## [1.6.522] 2025-07-02
- feat: Auth-Status bei App-Start aus localStorage laden und in Redux speichern
- fetchAuthService erstellt zum Auslesen von isAdminLoggedIn aus localStorage
- getAuthThunks Thunk implementiert zur Initialisierung von authSlice
- authSlice erweitert um setIsAdminLoggedIn Reducer
- dispatch(getAuthThunks()) in _app.tsx integriert für automatische Initialisierung bei App-Start
- Flackern und falscher Admin-Status nach Reload dauerhaft behoben
---
## [1.6.521] 2025-07-02
- refactor: Admin-Status direkt aus Redux ausgelesen und Props entfernt
- isAdminLoggedIn wird jetzt direkt aus authSlice im Redux-Store gelesen
- useAdminAuth und Prop-Weitergabe entfernt
- Flackern des Firmware-Buttons dauerhaft behoben
- Codestruktur vereinfacht und stabilisiert
---
## [1.6.520] 2025-07-02
- refactor: Admin-Status direkt aus Redux ausgelesen und Props entfernt
- isAdminLoggedIn wird jetzt direkt aus authSlice im Redux-Store gelesen
- useAdminAuth und Prop-Weitergabe entfernt
- Flackern des Firmware-Buttons dauerhaft behoben
- Codestruktur vereinfacht und stabilisiert
---
## [1.6.519] 2025-07-02
- fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
---
## [1.6.518] 2025-07-02
- fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
---
## [1.6.517] 2025-07-02
- fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
---
## [1.6.516] 2025-07-02
- fix: Firmware-Update-Button stabilisiert und Flackern entfernt
- useAdminAuth aus KueEinstellung entfernt und einmalig in SettingsModalWrapper ausgelagert
- isAdminLoggedIn als Prop übergeben, um ständige Aktualisierungen zu vermeiden
- Button wird jetzt stabil angezeigt ohne console-Logs oder Intervall-Aufrufe
---
## [1.6.515] 2025-07-02
- feat: Firmwareupdate für alle KÜ-Module mit Fortschrittsanzeige und Abschlussmeldung
- ProgressModal-Komponente implementiert, die während des Updates angezeigt wird
- Firmwareupdate dauert 5 Minuten (Mock-Simulation)
- Nach Abschluss erscheint automatisch ein Toast-Hinweis
- Verbesserte Benutzerführung durch blockierendes Modal während Update
- Logging in kueFirmwareUpdateLog.json integriert (Mock)
---
## [1.6.514] 2025-07-02
- feat: Firmwareupdate für alle KÜ-Module mit Fortschrittsanzeige und Abschlussmeldung
- ProgressModal-Komponente implementiert, die während des Updates angezeigt wird
- Firmwareupdate dauert 5 Minuten (Mock-Simulation)
- Nach Abschluss erscheint automatisch ein Toast-Hinweis
- Verbesserte Benutzerführung durch blockierendes Modal während Update
- Logging in kueFirmwareUpdateLog.json integriert (Mock)
---
## [1.6.513] 2025-07-01
- feat: alle KÜs Firmware update confirm
---
## [1.6.512] 2025-07-01
- fix: hide Firmware update button if admin not loged in

View File

@@ -0,0 +1,43 @@
// components/common/ConfirmModal.tsx
import React from "react";
interface ConfirmModalProps {
open: boolean;
title?: string;
message: string;
onConfirm: () => void;
onCancel: () => void;
}
export default function ConfirmModal({
open,
title,
message,
onConfirm,
onCancel,
}: ConfirmModalProps) {
if (!open) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-40 flex items-center justify-center z-50">
<div className="bg-white p-6 rounded shadow-xl w-[360px] max-w-full text-center">
{title && <h2 className="text-lg font-semibold mb-3">{title}</h2>}
<p className="mb-6 text-gray-800">{message}</p>
<div className="flex justify-center gap-4">
<button
className="bg-gray-300 hover:bg-gray-400 text-black px-4 py-2 rounded"
onClick={onCancel}
>
Abbrechen
</button>
<button
className="bg-littwin-blue hover:bg-blue-700 text-white px-4 py-2 rounded"
onClick={onConfirm}
>
Bestätigen
</button>
</div>
</div>
</div>
);
}

View File

@@ -1,24 +1,32 @@
// /komponents/main/kabelueberwachung/kue705FO/handlers/firmwareUpdate.ts
const firmwareUpdate = (slot: number) => {
// @/components/main/kabelueberwachung/kue705FO/handlers/firmwareUpdate.ts
export default async function firmwareUpdate(
slot: number
): Promise<{ message: string }> {
const isDev =
typeof window !== "undefined" && window.location.hostname === "localhost";
const url = isDev
? `${window.location.origin}/api/cpl/kueSingleModuleUpdateMock?slot=${
slot + 1
}`
: `${window.location.origin}/CPL?/kabelueberwachung.html&KSU${slot}=1`;
: `${window.location.origin}/CPL?Service/ae.ACP&KSU${slot}=1`;
fetch(url, { method: "GET" })
.then((response) => response.json())
.then((data) => {
alert(
data.message || `Update an Slot ${slot + 1} erfolgreich gestartet!`
);
})
.catch((error) => {
console.error("Fehler:", error);
alert("Fehler beim Update!");
});
};
try {
const response = await fetch(url, { method: "GET" });
export default firmwareUpdate;
if (!response.ok) {
throw new Error(`Fehler: Status ${response.status}`);
}
const data = await response.json();
//alert(data.message || `Update an Slot ${slot + 1} erfolgreich gestartet!`);
const message =
data.message || `Update an Slot ${slot + 1} erfolgreich gestartet!`;
console.log(message);
return { message };
} catch (error) {
console.error("Fehler:", error);
//alert("Fehler beim Update!");
return { message: "Fehler beim Update!" };
}
}

View File

@@ -1,12 +1,19 @@
"use client";
// components/main/kabelueberwachung/kue705FO/modals/KueEinstellung.tsx
import { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import type { RootState } from "../../../../../redux/store";
import type { RootState, AppDispatch } from "../../../../../redux/store";
import handleSave from "../handlers/handleSave";
import handleDisplayEinschalten from "../handlers/handleDisplayEinschalten";
import firmwareUpdate from "../handlers/firmwareUpdate";
import { useAdminAuth } from "../../../settingsPageComponents/hooks/useAdminAuth";
import ProgressModal from "@/components/main/settingsPageComponents/modals/ProgressModal";
import { toast } from "react-toastify";
import ConfirmModal from "@/components/common/ConfirmModal";
import {
openConfirmModal,
closeConfirmModal,
} from "@/redux/slices/confirmModalSlice";
import { startFirmwareUpdateThunk } from "@/redux/thunks/startFirmwareUpdateThunk";
interface Props {
slot: number;
@@ -29,11 +36,10 @@ const memoryIntervalOptions = [
export default function KueEinstellung({
slot,
onClose = () => {},
onModulNameChange,
}: Props) {
const dispatch = useDispatch();
const dispatch = useDispatch<AppDispatch>();
const {
kueID,
kueName,
@@ -43,18 +49,27 @@ export default function KueEinstellung({
kueLoopInterval,
memoryInterval,
} = useSelector((state: RootState) => state.kueDataSlice);
const reduxAdmin = useSelector(
(state: RootState) => state.authSlice.isAdminLoggedIn
);
const [isAdminLoggedIn] = useState(() => reduxAdmin);
const { isAdminLoggedIn } = useAdminAuth(true);
const showConfirmModal = useSelector(
(state: RootState) => state.confirmModal.open
);
const formCacheKey = `slot_${slot}`;
if (typeof window !== "undefined") {
window.__kueCache = window.__kueCache || {};
}
const cached =
typeof window !== "undefined" ? window.__kueCache?.[formCacheKey] : null;
const isUpdating = useSelector(
(state: RootState) => state.firmwareProgress.isUpdating
);
const progress = useSelector(
(state: RootState) => state.firmwareProgress.progress
);
const [formData, setFormData] = useState(() => {
if (cached) return cached;
if (typeof window !== "undefined") {
const cache = window.__kueCache?.[`slot_${slot}`];
if (cache) return cache;
}
return {
kueID: kueID[slot] || "",
kueName: kueName[slot] || "",
@@ -70,17 +85,12 @@ export default function KueEinstellung({
const updated = { ...formData, [key]: value };
setFormData(updated);
if (typeof window !== "undefined") {
window.__kueCache![formCacheKey] = updated;
window.__kueCache = window.__kueCache || {};
window.__kueCache[`slot_${slot}`] = updated;
}
};
const handleSaveWrapper = async () => {
const updatedKueID = [...kueID];
//updatedKueID[slot] = formData.kueID;
/* if (Object.isFrozen(kueID)) {
console.warn("kueID ist readonly!");
}
*/
const updatedKueName = [...kueName];
updatedKueName[slot] = formData.kueName;
@@ -100,7 +110,7 @@ export default function KueEinstellung({
updatedMemoryInterval[slot] = Number(formData.memoryInterval);
const newData = {
kueID: updatedKueID[slot],
kueID: kueID[slot],
kueName: updatedKueName[slot],
limit1: updatedLimit1[slot].toString(),
delay1: updatedDelay1[slot].toString(),
@@ -108,13 +118,11 @@ export default function KueEinstellung({
loopInterval: updatedLoopInterval[slot].toString(),
memoryInterval: updatedMemoryInterval[slot].toString(),
};
setFormData(newData);
if (typeof window !== "undefined") {
window.__kueCache![`slot_${slot}`] = newData;
}
// 🔧 handleSave aufrufen mit allen Daten
await handleSave({
slot,
ids: kueID,
@@ -122,7 +130,7 @@ export default function KueEinstellung({
isolationsgrenzwerte: updatedLimit1,
verzoegerung: updatedDelay1,
untereSchleifenGrenzwerte: updatedLimit2Low,
obereSchleifenGrenzwerte: updatedLimit2Low, // ggf. anpassen, falls du später High-Werte brauchst
obereSchleifenGrenzwerte: updatedLimit2Low,
schleifenintervall: updatedLoopInterval,
speicherintervall: updatedMemoryInterval,
originalValues: {
@@ -245,20 +253,37 @@ export default function KueEinstellung({
</div>
<div className="flex justify-end gap-2 p-0 rounded">
{isAdminLoggedIn && (
<button
onClick={() => {
if (
window.confirm(
"Warnung: Das Firmware-Update kann einige Minuten dauern und das Gerät neu starten.\nMöchten Sie wirklich fortfahren?"
)
) {
firmwareUpdate(slot);
<>
<button
onClick={() => dispatch(openConfirmModal())}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
Firmware Update
</button>
</>
)}
{showConfirmModal && (
<ConfirmModal
open={showConfirmModal}
title="Firmware-Update starten?"
message="⚠️ Das Firmware-Update kann einige Minuten dauern. Möchten Sie wirklich fortfahren?"
onCancel={() => dispatch(closeConfirmModal())}
onConfirm={async () => {
dispatch(closeConfirmModal());
toast.info("Firmware-Update gestartet. Bitte warten...");
dispatch(startFirmwareUpdateThunk(slot)); // Start Redux-Prozess
try {
await firmwareUpdate(slot);
} catch (err) {
console.error("Firmware-Update-Fehler:", err);
toast.error("❌ Fehler beim Firmwareupdate");
}
}}
className="bg-littwin-blue text-white px-4 py-2 rounded flex items-center"
>
Firmware Update
</button>
/>
)}
{isUpdating && (
<ProgressModal visible={isUpdating} progress={progress} />
)}
<button
onClick={() => handleDisplayEinschalten(slot)}

View File

@@ -38,7 +38,9 @@ export default function KueModal({ showModal, onClose, slot }: KueModalProps) {
window.kabelModalOpen = showModal;
}
}, [showModal]);
//-----------------------------------------------------
//------------------------------------------------------
return (
<ReactModal
isOpen={showModal}

View File

@@ -0,0 +1,55 @@
"use client";
// components/main/kabelueberwachung/kue705FO/modals/SuccessProgressModal.tsx
import React, { useEffect, useState } from "react";
interface Props {
visible: boolean;
duration?: number; // in Sekunden
onClose: () => void;
}
const SuccessProgressModal: React.FC<Props> = ({
visible,
duration = 10,
onClose,
}) => {
const [progress, setProgress] = useState(0);
useEffect(() => {
if (!visible) return;
setProgress(0);
const interval = setInterval(() => {
setProgress((prev) => {
if (prev >= 100) {
clearInterval(interval);
setTimeout(onClose, 500); // Schließen nach kurzer Verzögerung
return 100;
}
return prev + 100 / duration;
});
}, 1000);
return () => clearInterval(interval);
}, [visible, duration, onClose]);
if (!visible) return null;
return (
<div className="fixed inset-0 z-50 bg-black bg-opacity-40 flex items-center justify-center">
<div className="bg-white p-6 rounded-lg shadow-md text-center w-72">
<h2 className="text-lg font-bold text-green-600 mb-4">
Firmwareupdate erfolgreich abgeschlossen.
</h2>
<div className="w-full bg-gray-200 rounded h-3 overflow-hidden">
<div
className="h-3 bg-green-500 transition-all duration-100"
style={{ width: `${progress}%` }}
></div>
</div>
<p className="text-sm mt-2">{Math.floor(progress)}%</p>
</div>
</div>
);
};
export default SuccessProgressModal;

View File

@@ -13,12 +13,17 @@ import { getSystemSettingsThunk } from "../../../redux/thunks/getSystemSettingsT
import handleGeneralSubmit from "./handlers/handleGeneralSubmit";
import handleKueFirmwareUpdate from "@/components/main/settingsPageComponents/handlers/handleKueFirmwareUpdate";
import { useAdminAuth } from "@/components/main/settingsPageComponents/hooks/useAdminAuth";
import ProgressModal from "@/components/main/settingsPageComponents/modals/ProgressModal";
import "react-toastify/dist/ReactToastify.css";
const GeneralSettings: React.FC = () => {
const dispatch = useDispatch<AppDispatch>();
const systemSettings = useSelector(
(state: RootState) => state.systemSettingsSlice
);
const [isUpdating, setIsUpdating] = useState(false);
const [progress, setProgress] = useState(0);
const { isAdminLoggedIn } = useAdminAuth(true);
@@ -143,50 +148,6 @@ const GeneralSettings: React.FC = () => {
/>
</div>
{/* Admin Login */}
{/*
<div className="col-span-2 flex flex-col gap-1">
{isAdminLoggedIn ? (
<button
type="button"
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
onClick={logoutAdmin}
>
Admin abmelden
</button>
) : (
<>
<div className="flex flex-row gap-3">
<input
type="text"
placeholder="Benutzername"
className="border border-gray-300 rounded h-8 p-1 w-full text-xs"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
<input
type="password"
placeholder="Passwort"
className="border border-gray-300 rounded h-8 p-1 w-full text-xs"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
type="button"
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
onClick={handleLogin}
>
Admin anmelden
</button>
</div>
</>
)}
</div>
*/}
{/* Feedback */}
{/* You can add feedback here if needed */}
{/* Buttons */}
<div className="col-span-2 flex flex-wrap md:justify-between gap-1 mt-2">
<button
@@ -205,7 +166,45 @@ const GeneralSettings: React.FC = () => {
"⚠️ Wollen Sie wirklich ein Firmwareupdate für alle KÜ-Module starten?"
);
if (confirmed) {
handleKueFirmwareUpdate();
setIsUpdating(true);
setProgress(0);
const updateDuration = 300; // Sekunden (5 Minuten)
const intervalMs = 1000;
let elapsed = 0;
const interval = setInterval(() => {
elapsed++;
const newProgress = Math.min(
(elapsed / updateDuration) * 100,
100
);
setProgress(newProgress);
if (elapsed >= updateDuration) {
clearInterval(interval);
setIsUpdating(false);
}
}, intervalMs);
handleKueFirmwareUpdate()
.then(() => {
clearInterval(interval);
setProgress(100);
setTimeout(() => {
setIsUpdating(false);
setProgress(100);
setTimeout(() => {
alert("✅ Firmwareupdate erfolgreich abgeschlossen.");
}, 300); // Nach Modal-Schließung
}, 500);
})
.catch((error) => {
console.error("Update-Fehler:", error);
clearInterval(interval);
setIsUpdating(false);
});
}
}}
>
@@ -213,6 +212,8 @@ const GeneralSettings: React.FC = () => {
</button>
)}
<ProgressModal visible={isUpdating} progress={progress} />
<button
type="button"
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"

View File

@@ -11,7 +11,7 @@ const handleKueFirmwareUpdate = async () => {
const result = await res.text();
console.log("Firmwareupdate gesendet:", result);
alert("Firmwareupdate wurde an alle KÜ-Module gesendet.");
// alert("Firmwareupdate wurde an alle KÜ-Module gesendet.");
} catch (error) {
console.error("Fehler beim Firmwareupdate:", error);
alert("Fehler beim Firmwareupdate.");

View File

@@ -0,0 +1,28 @@
"use client";
import React from "react";
type Props = {
visible: boolean;
progress: number;
};
const ProgressModal: React.FC<Props> = ({ visible, progress }) => {
if (!visible) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white p-6 rounded shadow-md text-center w-80">
<h2 className="text-lg font-bold mb-4">Firmwareupdate läuft...</h2>
<div className="w-full bg-gray-200 rounded-full h-4">
<div
className="bg-blue-500 h-4 rounded-full transition-all duration-100"
style={{ width: `${progress}%` }}
></div>
</div>
<p className="mt-4 text-sm">{Math.round(progress)}% abgeschlossen</p>
</div>
</div>
);
};
export default ProgressModal;

View File

@@ -1,4 +1,69 @@
[
{
"timestamp": "2025-07-01T08:03:33.189Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:55:54.961Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:53:19.780Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:49:25.419Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:46:30.008Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:45:57.366Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:39:41.157Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:39:05.316Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:37:55.313Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:37:20.818Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T07:26:45.785Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T06:19:21.460Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T06:06:58.694Z",
"command": "&KSU99=1",
"message": "Firmwareupdate an alle KÜ-Module ausgelöst (Mock)"
},
{
"timestamp": "2025-07-01T05:48:51.161Z",
"command": "&KSU99=1",

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "cpl-v4",
"version": "1.6.512",
"version": "1.6.526",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cpl-v4",
"version": "1.6.512",
"version": "1.6.526",
"dependencies": {
"@fontsource/roboto": "^5.1.0",
"@iconify-icons/ri": "^1.2.10",

View File

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

View File

@@ -23,6 +23,14 @@ import { getReferenceCurveBySlotThunk } from "@/redux/thunks/getReferenceCurveBy
import { getAllTDRReferenceChartThunk } from "@/redux/thunks/getAllTDRReferenceChartThunk";
import { getTDRChartDataByIdThunk } from "@/redux/thunks/getTDRChartDataByIdThunk";
import { getLoopChartDataThunk } from "@/redux/thunks/getLoopChartDataThunk";
import { getAuthThunks } from "@/redux/thunks/getAuthThunks";
import Modal from "react-modal";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
if (typeof window !== "undefined") {
Modal.setAppElement("#__next"); // oder "#root", je nach App-Struktur
}
import "@/styles/globals.css";
@@ -50,6 +58,7 @@ function AppContent({
const pathname = window.location.pathname;
const loadAndDispatch = () => {
dispatch(getAuthThunks());
if (pathname.includes("kabelueberwachung")) {
dispatch(getKueDataThunk());
} else if (pathname.includes("digitalOutputs")) {
@@ -107,6 +116,7 @@ function AppContent({
</div>
)}
<Component {...pageProps} />
<ToastContainer position="top-right" autoClose={3000} />
</main>
</div>
<Footer />

View File

@@ -1,8 +1,15 @@
// pages/api/cpl/kueFirmwareUpdateMock.ts
import type { NextApiRequest, NextApiResponse } from "next";
import fs from "fs";
import path from "path";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// Hilfsfunktion für künstliche Verzögerung
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const logFilePath = path.join(
process.cwd(),
@@ -22,22 +29,22 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
existingLog = JSON.parse(fileContent);
}
// Letzten 50 Einträge speichern
const updatedLog = [logEntry, ...existingLog].slice(0, 50);
fs.writeFileSync(logFilePath, JSON.stringify(updatedLog, null, 2), "utf-8");
console.log("🕒 Starte Firmware-Mock-Wartezeit (5 Minuten)...");
await delay(10000); // 5 Minuten simulieren (300.000 ms)
console.log("✅ Firmwareupdate-Mock abgeschlossen.");
res.status(200).json({
status: "success",
log: logEntry,
});
} catch (error) {
console.error("Fehler beim Firmwareupdate-Mock:", error);
res
.status(500)
.json({
status: "error",
message: "Fehler beim Speichern des Firmwareupdates",
});
console.error("Fehler beim Firmwareupdate-Mock:", error);
res.status(500).json({
status: "error",
message: "Fehler beim Speichern des Firmwareupdates",
});
}
}

View File

@@ -3,17 +3,25 @@ import type { NextApiRequest, NextApiResponse } from "next";
import fs from "fs";
import path from "path";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// Hilfsfunktion für künstliche Verzögerung
const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const filePath = path.join(
process.cwd(),
"mocks/device-cgi-simulator/firmwareUpdate/singleModuleUpdateResponse.json"
);
try {
// ⏱️ 10 Sekunden warten
await delay(25000); // 5 Minuten simulieren (300.000 ms)
const fileContents = fs.readFileSync(filePath, "utf-8");
const responseData = JSON.parse(fileContents);
// Optional: slot aus query übernehmen
const slot = req.query.slot ?? "X";
responseData.message = `Update erfolgreich gestartet für Slot ${slot}`;

View File

@@ -0,0 +1,29 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface ConfirmModalState {
open: boolean;
}
const initialState: ConfirmModalState = {
open: false,
};
export const confirmModalSlice = createSlice({
name: "confirmModal",
initialState,
reducers: {
openConfirmModal: (state) => {
state.open = true;
},
closeConfirmModal: (state) => {
state.open = false;
},
setConfirmModal: (state, action: PayloadAction<boolean>) => {
state.open = action.payload;
},
},
});
export const { openConfirmModal, closeConfirmModal, setConfirmModal } =
confirmModalSlice.actions;
export default confirmModalSlice.reducer;

View File

@@ -0,0 +1,28 @@
// redux/slices/firmwareProgressSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface State {
progress: number;
isUpdating: boolean;
}
const initialState: State = {
progress: 0,
isUpdating: false,
};
export const firmwareProgressSlice = createSlice({
name: "firmwareProgress",
initialState,
reducers: {
setProgress: (state, action: PayloadAction<number>) => {
state.progress = action.payload;
},
setIsUpdating: (state, action: PayloadAction<boolean>) => {
state.isUpdating = action.payload;
},
},
});
export const { setProgress, setIsUpdating } = firmwareProgressSlice.actions;
export default firmwareProgressSlice.reducer;

View File

@@ -0,0 +1,86 @@
// redux/slices/firmwareUpdateSlice.ts
import { createSlice, createAsyncThunk, PayloadAction } from "@reduxjs/toolkit";
import firmwareUpdate from "@/components/main/kabelueberwachung/kue705FO/handlers/firmwareUpdate";
interface FirmwareUpdateState {
isUpdating: boolean;
progress: number;
status: "idle" | "loading" | "success" | "error";
message: string;
}
const initialState: FirmwareUpdateState = {
isUpdating: false,
progress: 0,
status: "idle",
message: "",
};
export const startFirmwareUpdateThunk = createAsyncThunk(
"firmware/update",
async (slot: number, { dispatch, rejectWithValue }) => {
try {
const totalDuration = 5000;
const intervalMs = 50;
const steps = totalDuration / intervalMs;
let currentStep = 0;
dispatch(setUpdating(true));
const interval = setInterval(() => {
currentStep++;
const newProgress = Math.min((currentStep / steps) * 100, 100);
dispatch(setProgress(newProgress));
if (currentStep >= steps) clearInterval(interval);
}, intervalMs);
const response = await firmwareUpdate(slot);
if (response.message.includes("erfolgreich")) {
return response.message;
} else {
return rejectWithValue("Update fehlgeschlagen");
}
} catch (err) {
console.error("Fehler beim Firmwareupdate:", err);
return rejectWithValue("Fehler beim Firmwareupdate");
}
}
);
const firmwareUpdateSlice = createSlice({
name: "firmwareUpdate",
initialState,
reducers: {
setUpdating: (state, action: PayloadAction<boolean>) => {
state.isUpdating = action.payload;
},
setProgress: (state, action: PayloadAction<number>) => {
state.progress = action.payload;
},
resetFirmwareState: () => initialState,
},
extraReducers: (builder) => {
builder
.addCase(startFirmwareUpdateThunk.pending, (state) => {
state.status = "loading";
state.message = "Update gestartet";
})
.addCase(startFirmwareUpdateThunk.fulfilled, (state, action) => {
state.status = "success";
state.message = action.payload;
state.isUpdating = false;
state.progress = 100;
})
.addCase(startFirmwareUpdateThunk.rejected, (state, action) => {
state.status = "error";
state.message = action.payload as string;
state.isUpdating = false;
state.progress = 100;
});
},
});
export const { setUpdating, setProgress, resetFirmwareState } =
firmwareUpdateSlice.actions;
export default firmwareUpdateSlice.reducer;

View File

@@ -26,6 +26,9 @@ import systemVoltTempReducer from "./slices/systemVoltTempSlice";
import analogInputsHistoryReducer from "./slices/analogInputsHistorySlice";
import selectedAnalogInputReducer from "./slices/selectedAnalogInputSlice";
import messagesReducer from "./slices/messagesSlice";
import firmwareUpdateReducer from "@/redux/slices/firmwareUpdateSlice";
import confirmModalReducer from "./slices/confirmModalSlice";
import firmwareProgressReducer from "./slices/firmwareProgressSlice";
const store = configureStore({
reducer: {
@@ -54,6 +57,9 @@ const store = configureStore({
analogInputsHistory: analogInputsHistoryReducer,
selectedAnalogInput: selectedAnalogInputReducer,
messages: messagesReducer,
firmwareUpdate: firmwareUpdateReducer,
confirmModal: confirmModalReducer,
firmwareProgress: firmwareProgressReducer,
},
});

View File

@@ -0,0 +1,12 @@
// redux/thunks/getAuthThunks.ts
import { createAsyncThunk } from "@reduxjs/toolkit";
import { fetchAuthService } from "@/services/fetchAuthService";
import { setAdminLoggedIn } from "@/redux/slices/authSlice";
export const getAuthThunks = createAsyncThunk(
"auth/getAuthThunks",
async (_, { dispatch }) => {
const { isAdminLoggedIn } = fetchAuthService();
dispatch(setAdminLoggedIn(isAdminLoggedIn)); // boolean, nicht string!
}
);

View File

@@ -0,0 +1,31 @@
// redux/thunks/startFirmwareUpdateThunk.ts
import { AppDispatch } from "../store";
import {
setProgress,
setIsUpdating,
} from "@/redux/slices/firmwareProgressSlice";
export const startFirmwareUpdateThunk =
(slot: number) => async (dispatch: AppDispatch) => {
dispatch(setIsUpdating(true));
dispatch(setProgress(0));
const totalDuration = 5 * 60 * 1000;
const intervalMs = 1000;
const steps = totalDuration / intervalMs;
let currentStep = 0;
const interval = setInterval(() => {
currentStep++;
const percent = Math.min((currentStep / steps) * 100, 100);
dispatch(setProgress(percent));
if (currentStep >= steps) {
clearInterval(interval);
dispatch(setProgress(100));
setTimeout(() => {
dispatch(setIsUpdating(false));
}, 500);
}
}, intervalMs);
};

View File

@@ -0,0 +1,6 @@
// services/fetchAuthService.ts
export const fetchAuthService = () => {
const isAdminLoggedInRaw = localStorage.getItem("isAdminLoggedIn");
const isAdminLoggedIn = isAdminLoggedInRaw === "true"; // < explizit Boolean
return { isAdminLoggedIn };
};