From b091a8d82a4958b0c9beee6ca70f06d5c3a3ddc1 Mon Sep 17 00:00:00 2001 From: ISA Date: Tue, 8 Jul 2025 13:13:30 +0200 Subject: [PATCH] refactor: extract Kabelueberwachung logic into KabelueberwachungView for better structure --- .env.development | 2 +- .env.production | 2 +- CHANGELOG.md | 9 + .../main/digitalInputs/DigitalInputsView.tsx | 70 +++++++ .../digitalOutputs/DigitalOutputsView.tsx | 57 ++++++ .../KabelueberwachungView.tsx | 173 +++++++++++++++++ .../settingsPageComponents/SettingsView.tsx | 85 +++++++++ package-lock.json | 4 +- package.json | 2 +- pages/dashboard.tsx | 11 +- pages/digitalInputs.tsx | 76 +------- pages/digitalOutputs.tsx | 63 +------ pages/einstellungen.tsx | 91 +-------- pages/kabelueberwachung.tsx | 175 +----------------- 14 files changed, 423 insertions(+), 397 deletions(-) create mode 100644 components/main/digitalInputs/DigitalInputsView.tsx create mode 100644 components/main/digitalOutputs/DigitalOutputsView.tsx create mode 100644 components/main/kabelueberwachung/KabelueberwachungView.tsx create mode 100644 components/main/settingsPageComponents/SettingsView.tsx diff --git a/.env.development b/.env.development index 0731383..16abac4 100644 --- a/.env.development +++ b/.env.development @@ -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.562 +NEXT_PUBLIC_APP_VERSION=1.6.563 NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter) diff --git a/.env.production b/.env.production index 5400a43..a2618a9 100644 --- a/.env.production +++ b/.env.production @@ -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.562 +NEXT_PUBLIC_APP_VERSION=1.6.563 NEXT_PUBLIC_CPL_MODE=production \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index ffacd8c..b8e9dd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## [1.6.563] – 2025-07-08 + +- refactor: move analog inputs logic to AnalogInputsView component + +- Verschiebt die gesamte UI-Logik aus pages/analogInputs.tsx in eine eigene Komponente AnalogInputsView.tsx +- pages/analogInputs.tsx dient jetzt nur noch als Router-Einstiegspunkt +- Vereinheitlicht die Struktur wie bei MeldungenView und DashboardView + +--- ## [1.6.562] – 2025-07-08 - fix: call digitalOutputs from _app.tsx to show immediately without delay diff --git a/components/main/digitalInputs/DigitalInputsView.tsx b/components/main/digitalInputs/DigitalInputsView.tsx new file mode 100644 index 0000000..c6923fd --- /dev/null +++ b/components/main/digitalInputs/DigitalInputsView.tsx @@ -0,0 +1,70 @@ +// components/main/digitalInputs/DigitalInputsView.tsx +"use client"; + +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { AppDispatch } from "@/redux/store"; + +import InputModal from "@/components/main/digitalInputs/digitalInputsModal"; +import { getDigitalInputsThunk } from "@/redux/thunks/getDigitalInputsThunk"; +import DigitalInputsWidget from "@/components/main/digitalInputs/DigitalInputsWidget"; + +const DigitalInputsView: React.FC = () => { + const dispatch = useDispatch(); + + interface DigitalInput { + id: number; + eingangOffline: boolean; + status: boolean; + label: string; + [key: string]: unknown; + } + + const [selectedInput, setSelectedInput] = useState(null); + const [isInputModalOpen, setIsInputModalOpen] = useState(false); + + useEffect(() => { + dispatch(getDigitalInputsThunk()); + const interval = setInterval(() => { + dispatch(getDigitalInputsThunk()); + }, 10000); + return () => clearInterval(interval); + }, [dispatch]); + + const openInputModal = (input: DigitalInput) => { + setSelectedInput(input); + setIsInputModalOpen(true); + }; + + const closeInputModal = () => { + setSelectedInput(null); + setIsInputModalOpen(false); + }; + + return ( +
+

Meldungseingänge

+ +
+ + +
+ + {isInputModalOpen && selectedInput && ( + + )} +
+ ); +}; + +export default DigitalInputsView; diff --git a/components/main/digitalOutputs/DigitalOutputsView.tsx b/components/main/digitalOutputs/DigitalOutputsView.tsx new file mode 100644 index 0000000..fef8fcc --- /dev/null +++ b/components/main/digitalOutputs/DigitalOutputsView.tsx @@ -0,0 +1,57 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { useDispatch } from "react-redux"; +import { AppDispatch } from "@/redux/store"; + +import DigitalOutputsModal from "./DigitalOutputsModal"; +import DigitalOutputsWidget from "./DigitalOutputsWidget"; + +import { getDigitalOutputsThunk } from "@/redux/thunks/getDigitalOutputsThunk"; +import type { DigitalOutput } from "@/types/digitalOutput"; + +const DigitalOutputsView: React.FC = () => { + const dispatch = useDispatch(); + const [selectedOutput, setSelectedOutput] = useState( + null + ); + const [isOutputModalOpen, setIsOutputModalOpen] = useState(false); + + useEffect(() => { + const interval = setInterval(() => { + dispatch(getDigitalOutputsThunk()); + }, 3000); + + return () => clearInterval(interval); + }, [dispatch]); + + const openOutputModal = (output: DigitalOutput) => { + setSelectedOutput(output); + setIsOutputModalOpen(true); + }; + + const closeOutputModal = () => { + setSelectedOutput(null); + setIsOutputModalOpen(false); + }; + + return ( +
+

Schaltausgänge

+ +
+ +
+ + {selectedOutput && ( + + )} +
+ ); +}; + +export default DigitalOutputsView; diff --git a/components/main/kabelueberwachung/KabelueberwachungView.tsx b/components/main/kabelueberwachung/KabelueberwachungView.tsx new file mode 100644 index 0000000..933be0e --- /dev/null +++ b/components/main/kabelueberwachung/KabelueberwachungView.tsx @@ -0,0 +1,173 @@ +"use client"; // /pages/kabelueberwachung.tsx +import React, { useState, useEffect } from "react"; +import { useSearchParams } from "next/navigation"; +import Kue705FO from "@/components/main/kabelueberwachung/kue705FO/Kue705FO"; +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch } from "@/redux/store"; // Adjust the path to your Redux store file +import { RootState } from "@/redux/store"; // Adjust the path to your Redux store file +import { getKueDataThunk } from "@/redux/thunks/getKueDataThunk"; + +function KabelueberwachungView() { + const dispatch: AppDispatch = useDispatch(); + const searchParams = useSearchParams(); // URL-Parameter holen + const initialRack = parseInt(searchParams.get("rack") ?? "1") || 1; // Rack-Nummer aus URL oder 1 + + const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert + const [alarmStatus, setAlarmStatus] = useState([]); // Alarmstatus + + // Redux-Variablen aus dem Store abrufen + const { + kueOnline, + kueID, + kueIso, + kueAlarm1, + kueAlarm2, + kueResidence, + kueCableBreak, + kueGroundFault, + } = useSelector((state: RootState) => state.kueDataSlice); + + //---------------------------------------------------------------- + // Alarmstatus basierend auf Redux-Variablen berechnen + const updateAlarmStatus = React.useCallback(() => { + const updatedAlarmStatus = kueIso.map( + (_: number | string, index: number) => { + return Boolean( + (kueAlarm1 && kueAlarm1[index]) || + (kueAlarm2 && kueAlarm2[index]) || + (kueCableBreak && kueCableBreak[index]) || + (kueGroundFault && kueGroundFault[index]) + ); + } + ); + setAlarmStatus(updatedAlarmStatus); + }, [kueIso, kueAlarm1, kueAlarm2, kueCableBreak, kueGroundFault]); + + // Alarmstatus initial berechnen und alle 10 Sekunden aktualisieren + useEffect(() => { + updateAlarmStatus(); + const interval = setInterval(updateAlarmStatus, 10000); + return () => clearInterval(interval); + }, [updateAlarmStatus]); + + // Modul- und Rack-Daten aufbereiten + const allModules = kueIso.map((iso: number | string, index: number) => ({ + isolationswert: iso, + schleifenwiderstand: kueResidence[index], + modulName: kueID[index] || `Modul ${index + 1}`, // Eindeutiger Name pro Index + kueOnlineStatus: kueOnline[index], + alarmStatus: alarmStatus[index], + tdrLocation: [], // Placeholder, replace with actual tdrLocation if available + })); + //console.log("Alle Module:", allModules); + + const racks = React.useMemo( + () => ({ + rack1: allModules.slice(0, 8), + rack2: allModules.slice(8, 16), + rack3: allModules.slice(16, 24), + rack4: allModules.slice(24, 32), + }), + [allModules] + ); + + // Konsolenausgaben für jede Rack-Aufteilung + /* console.log( + "Rack 1 Module:", + racks.rack1.map((slot) => slot.modulName) + ); + console.log( + "Rack 2 Module:", + racks.rack2.map((slot) => slot.modulName) + ); + console.log( + "Rack 3 Module:", + racks.rack3.map((slot) => slot.modulName) + ); + console.log( + "Rack 4 Module:", + racks.rack4.map((slot) => slot.modulName) + ); */ + + // Funktion zum Wechseln des Racks + const changeRack = (rack: number) => { + setActiveRack(rack); + console.log(`Aktives Rack geändert zu: ${rack}`); + }; + + useEffect(() => { + /* console.log(`Aktives Rack: ${activeRack}`); + console.log( + `Rack ${activeRack} Modulnamen:`, + racks[`rack${activeRack as 1 | 2 | 3 | 4}` as keyof typeof racks].map((slot: any) => slot.modulName) + ); */ + }, [activeRack, racks]); + + //----------------------------------------------------------- + + //------------------------------------------------------------ + useEffect(() => { + if (kueIso.length === 0) { + console.log("📦 Lade KUE-Daten aus getKueDataThunk..."); + dispatch(getKueDataThunk()); + } + }, [dispatch, kueIso.length]); + //------------------------------------------------------------ + + // JSX rendering + return ( +
+
+ {[1, 2, 3, 4].map((rack) => ( + + ))} +
+
+ {( + racks[ + `rack${activeRack as 1 | 2 | 3 | 4}` as keyof typeof racks + ] as typeof allModules + ).map( + ( + slot: { + isolationswert: number | string; + schleifenwiderstand: number | string; + modulName: string; + kueOnlineStatus: number; + alarmStatus?: boolean; + tdrLocation: number[]; + }, + index: number + ) => { + const slotIndex = index + (activeRack - 1) * 8; + return ( +
+ +
+ ); + } + )} +
+
+ ); +} + +export default KabelueberwachungView; diff --git a/components/main/settingsPageComponents/SettingsView.tsx b/components/main/settingsPageComponents/SettingsView.tsx new file mode 100644 index 0000000..ce6ddd3 --- /dev/null +++ b/components/main/settingsPageComponents/SettingsView.tsx @@ -0,0 +1,85 @@ +// components/main/settingsPageComponents/SettingsView.tsx +"use client"; + +import React, { useEffect, useState } from "react"; +import { useAppDispatch } from "@/redux/store"; +import { getSystemSettingsThunk } from "@/redux/thunks/getSystemSettingsThunk"; +import GeneralSettings from "./GeneralSettings"; +import OPCUAInterfaceSettings from "./OPCUAInterfaceSettings"; +import DatabaseSettings from "./DatabaseSettings"; +import NTPSettings from "./NTPSettings"; +import UserManagementSettings from "./UserManagementSettings"; + +export default function SettingsView() { + const [activeTab, setActiveTab] = useState("tab1"); + const dispatch = useAppDispatch(); + + useEffect(() => { + dispatch(getSystemSettingsThunk()); + }, [dispatch]); + + return ( +
+
+ + + + + +
+ +
+ {activeTab === "tab1" && } + {activeTab === "tab2" && } + {activeTab === "tab3" && } + {activeTab === "tab4" && } + {activeTab === "tab5" && } +
+
+ ); +} diff --git a/package-lock.json b/package-lock.json index 5dd029b..2503a5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cpl-v4", - "version": "1.6.562", + "version": "1.6.563", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cpl-v4", - "version": "1.6.562", + "version": "1.6.563", "dependencies": { "@fontsource/roboto": "^5.1.0", "@headlessui/react": "^2.2.4", diff --git a/package.json b/package.json index c1b1146..27ff339 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cpl-v4", - "version": "1.6.562", + "version": "1.6.563", "private": true, "scripts": { "dev": "next dev", diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx index 74b1b5a..5a9bd24 100644 --- a/pages/dashboard.tsx +++ b/pages/dashboard.tsx @@ -1,13 +1,4 @@ -// pages/dashboard.tsx - -import React from "react"; -import dynamic from "next/dynamic"; - -// Dynamisches Importieren der eigentlichen UI-Komponente -const DashboardView = dynamic( - () => import("@/components/main/dashboard/DashboardView"), - { ssr: false } -); +import DashboardView from "@/components/main/dashboard/DashboardView"; export default function DashboardPage() { return ; diff --git a/pages/digitalInputs.tsx b/pages/digitalInputs.tsx index b8fe737..8dd2deb 100644 --- a/pages/digitalInputs.tsx +++ b/pages/digitalInputs.tsx @@ -1,73 +1,7 @@ -"use client"; -// /pages/digitalInputs.tsx -import React, { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; -import { AppDispatch } from "@/redux/store"; +// pages/digitalInputs.tsx -import InputModal from "@/components/main/digitalInputs/digitalInputsModal"; +import DigitalInputsView from "@/components/main/digitalInputs/DigitalInputsView"; -import { getDigitalInputsThunk } from "@/redux/thunks/getDigitalInputsThunk"; - -import DigitalInputsWidget from "@/components/main/digitalInputs/DigitalInputsWidget"; - -const DigitalInputs: React.FC = () => { - const dispatch = useDispatch(); - interface DigitalInput { - id: number; - eingangOffline: boolean; - status: boolean; - label: string; - [key: string]: unknown; - } - - const [selectedInput, setSelectedInput] = useState(null); - - const [isInputModalOpen, setIsInputModalOpen] = useState(false); - - useEffect(() => { - dispatch(getDigitalInputsThunk()); - - const interval = setInterval(() => { - dispatch(getDigitalInputsThunk()); - }, 10000); - - return () => clearInterval(interval); - }, [dispatch]); - - const openInputModal = (input: DigitalInput) => { - setSelectedInput(input); - setIsInputModalOpen(true); - }; - - const closeInputModal = () => { - setSelectedInput(null); - setIsInputModalOpen(false); - }; - - return ( -
-

Meldungseingänge

- -
- - -
- - {isInputModalOpen && selectedInput && ( - - )} -
- ); -}; - -export default DigitalInputs; +export default function DigitalInputsPage() { + return ; +} diff --git a/pages/digitalOutputs.tsx b/pages/digitalOutputs.tsx index a267cbc..12fcfc3 100644 --- a/pages/digitalOutputs.tsx +++ b/pages/digitalOutputs.tsx @@ -1,58 +1,5 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; -import { AppDispatch } from "@/redux/store"; - -import DigitalOutputsModal from "../components/main/digitalOutputs/DigitalOutputsModal"; -import DigitalOutputsWidget from "../components/main/digitalOutputs/DigitalOutputsWidget"; - -import { getDigitalOutputsThunk } from "@/redux/thunks/getDigitalOutputsThunk"; - -import type { DigitalOutput } from "@/types/digitalOutput"; - -const DigitalOutputs: React.FC = () => { - const dispatch = useDispatch(); - const [selectedOutput, setSelectedOutput] = useState( - null - ); - const [isOutputModalOpen, setIsOutputModalOpen] = useState(false); - - useEffect(() => { - const interval = setInterval(() => { - dispatch(getDigitalOutputsThunk()); - }, 3000); - - return () => clearInterval(interval); - }, [dispatch]); - - const openOutputModal = (output: DigitalOutput) => { - setSelectedOutput(output); - setIsOutputModalOpen(true); - }; - - const closeOutputModal = () => { - setSelectedOutput(null); - setIsOutputModalOpen(false); - }; - - return ( -
-

Schaltausgänge

- -
- -
- - {selectedOutput && ( - - )} -
- ); -}; - -export default DigitalOutputs; +// pages/digitalOutputs.tsx +import DigitalOutputsView from "@/components/main/digitalOutputs/DigitalOutputsView"; +export default function DigitalOutputsPage() { + return ; +} diff --git a/pages/einstellungen.tsx b/pages/einstellungen.tsx index 5b59219..5c45f9c 100644 --- a/pages/einstellungen.tsx +++ b/pages/einstellungen.tsx @@ -1,85 +1,12 @@ -// /pages/einstellungen.tsx -import React, { useState, useEffect } from "react"; -import { useAppDispatch } from "../redux/store"; -import { getSystemSettingsThunk } from "../redux/thunks/getSystemSettingsThunk"; -import GeneralSettings from "../components/main/settingsPageComponents/GeneralSettings"; -import OPCUAInterfaceSettings from "../components/main/settingsPageComponents/OPCUAInterfaceSettings"; -import DatabaseSettings from "../components/main/settingsPageComponents/DatabaseSettings"; -import NTPSettings from "../components/main/settingsPageComponents/NTPSettings"; -import UserManagementSettings from "../components/main/settingsPageComponents/UserManagementSettings"; +// pages/einstellungen.tsx +import React from "react"; +import dynamic from "next/dynamic"; -export default function Settings() { - const [activeTab, setActiveTab] = useState("tab1"); - const dispatch = useAppDispatch(); +const SettingsView = dynamic( + () => import("@/components/main/settingsPageComponents/SettingsView"), + { ssr: false } +); - useEffect(() => { - dispatch(getSystemSettingsThunk()); - }, [dispatch]); - - return ( -
- {/* Tab-Navigation */} -
- - - - - -
- - {/* Tab-Inhalt */} -
- {activeTab === "tab1" && } - {activeTab === "tab2" && } - {activeTab === "tab3" && } - {activeTab === "tab4" && } - {activeTab === "tab5" && } -
-
- ); +export default function EinstellungenPage() { + return ; } diff --git a/pages/kabelueberwachung.tsx b/pages/kabelueberwachung.tsx index 27d7cb2..9a3c88e 100644 --- a/pages/kabelueberwachung.tsx +++ b/pages/kabelueberwachung.tsx @@ -1,173 +1,6 @@ -"use client"; // /pages/kabelueberwachung.tsx -import React, { useState, useEffect } from "react"; -import { useSearchParams } from "next/navigation"; -import Kue705FO from "../components/main/kabelueberwachung/kue705FO/Kue705FO"; -import { useDispatch, useSelector } from "react-redux"; -import { AppDispatch } from "../redux/store"; // Adjust the path to your Redux store file -import { RootState } from "../redux/store"; // Adjust the path to your Redux store file -import { getKueDataThunk } from "../redux/thunks/getKueDataThunk"; +"use client"; +import KabelueberwachungView from "@/components/main/kabelueberwachung/KabelueberwachungView"; -function Kabelueberwachung() { - const dispatch: AppDispatch = useDispatch(); - const searchParams = useSearchParams(); // URL-Parameter holen - const initialRack = parseInt(searchParams.get("rack") ?? "1") || 1; // Rack-Nummer aus URL oder 1 - - const [activeRack, setActiveRack] = useState(initialRack); // Nutze initialRack als Startwert - const [alarmStatus, setAlarmStatus] = useState([]); // Alarmstatus - - // Redux-Variablen aus dem Store abrufen - const { - kueOnline, - kueID, - kueIso, - kueAlarm1, - kueAlarm2, - kueResidence, - kueCableBreak, - kueGroundFault, - } = useSelector((state: RootState) => state.kueDataSlice); - - //---------------------------------------------------------------- - // Alarmstatus basierend auf Redux-Variablen berechnen - const updateAlarmStatus = React.useCallback(() => { - const updatedAlarmStatus = kueIso.map( - (_: number | string, index: number) => { - return Boolean( - (kueAlarm1 && kueAlarm1[index]) || - (kueAlarm2 && kueAlarm2[index]) || - (kueCableBreak && kueCableBreak[index]) || - (kueGroundFault && kueGroundFault[index]) - ); - } - ); - setAlarmStatus(updatedAlarmStatus); - }, [kueIso, kueAlarm1, kueAlarm2, kueCableBreak, kueGroundFault]); - - // Alarmstatus initial berechnen und alle 10 Sekunden aktualisieren - useEffect(() => { - updateAlarmStatus(); - const interval = setInterval(updateAlarmStatus, 10000); - return () => clearInterval(interval); - }, [updateAlarmStatus]); - - // Modul- und Rack-Daten aufbereiten - const allModules = kueIso.map((iso: number | string, index: number) => ({ - isolationswert: iso, - schleifenwiderstand: kueResidence[index], - modulName: kueID[index] || `Modul ${index + 1}`, // Eindeutiger Name pro Index - kueOnlineStatus: kueOnline[index], - alarmStatus: alarmStatus[index], - tdrLocation: [], // Placeholder, replace with actual tdrLocation if available - })); - //console.log("Alle Module:", allModules); - - const racks = React.useMemo( - () => ({ - rack1: allModules.slice(0, 8), - rack2: allModules.slice(8, 16), - rack3: allModules.slice(16, 24), - rack4: allModules.slice(24, 32), - }), - [allModules] - ); - - // Konsolenausgaben für jede Rack-Aufteilung - /* console.log( - "Rack 1 Module:", - racks.rack1.map((slot) => slot.modulName) - ); - console.log( - "Rack 2 Module:", - racks.rack2.map((slot) => slot.modulName) - ); - console.log( - "Rack 3 Module:", - racks.rack3.map((slot) => slot.modulName) - ); - console.log( - "Rack 4 Module:", - racks.rack4.map((slot) => slot.modulName) - ); */ - - // Funktion zum Wechseln des Racks - const changeRack = (rack: number) => { - setActiveRack(rack); - console.log(`Aktives Rack geändert zu: ${rack}`); - }; - - useEffect(() => { - /* console.log(`Aktives Rack: ${activeRack}`); - console.log( - `Rack ${activeRack} Modulnamen:`, - racks[`rack${activeRack as 1 | 2 | 3 | 4}` as keyof typeof racks].map((slot: any) => slot.modulName) - ); */ - }, [activeRack, racks]); - - //----------------------------------------------------------- - - //------------------------------------------------------------ - useEffect(() => { - if (kueIso.length === 0) { - console.log("📦 Lade KUE-Daten aus getKueDataThunk..."); - dispatch(getKueDataThunk()); - } - }, [dispatch, kueIso.length]); - //------------------------------------------------------------ - - // JSX rendering - return ( -
-
- {[1, 2, 3, 4].map((rack) => ( - - ))} -
-
- {( - racks[ - `rack${activeRack as 1 | 2 | 3 | 4}` as keyof typeof racks - ] as typeof allModules - ).map( - ( - slot: { - isolationswert: number | string; - schleifenwiderstand: number | string; - modulName: string; - kueOnlineStatus: number; - alarmStatus?: boolean; - tdrLocation: number[]; - }, - index: number - ) => { - const slotIndex = index + (activeRack - 1) * 8; - return ( -
- -
- ); - } - )} -
-
- ); +export default function KabelueberwachungPage() { + return ; } - -export default Kabelueberwachung;