refactor: extract Kabelueberwachung logic into KabelueberwachungView for better structure
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
70
components/main/digitalInputs/DigitalInputsView.tsx
Normal file
70
components/main/digitalInputs/DigitalInputsView.tsx
Normal file
@@ -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<AppDispatch>();
|
||||
|
||||
interface DigitalInput {
|
||||
id: number;
|
||||
eingangOffline: boolean;
|
||||
status: boolean;
|
||||
label: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const [selectedInput, setSelectedInput] = useState<DigitalInput | null>(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 (
|
||||
<div className="flex flex-col gap-3 p-4 h-[calc(100vh-13vh-8vh)] laptop:h-[calc(100vh-10vh-5vh)] xl:h-[calc(100vh-10vh-6vh)] laptop:gap-0">
|
||||
<h1 className="text-base font-semibold mb-2">Meldungseingänge</h1>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-start ">
|
||||
<DigitalInputsWidget
|
||||
openInputModal={openInputModal}
|
||||
inputRange={{ start: 0, end: 16 }}
|
||||
/>
|
||||
<DigitalInputsWidget
|
||||
openInputModal={openInputModal}
|
||||
inputRange={{ start: 16, end: 32 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isInputModalOpen && selectedInput && (
|
||||
<InputModal
|
||||
selectedInput={selectedInput}
|
||||
closeInputModal={closeInputModal}
|
||||
isOpen={isInputModalOpen}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigitalInputsView;
|
||||
57
components/main/digitalOutputs/DigitalOutputsView.tsx
Normal file
57
components/main/digitalOutputs/DigitalOutputsView.tsx
Normal file
@@ -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<AppDispatch>();
|
||||
const [selectedOutput, setSelectedOutput] = useState<DigitalOutput | null>(
|
||||
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 (
|
||||
<div className="flex flex-col gap-3 p-4 h-[calc(100vh-13vh-8vh)] laptop:h-[calc(100vh-10vh-5vh)] xl:h-[calc(100vh-10vh-6vh)] laptop:gap-0">
|
||||
<h1 className="text-base font-semibold mb-2">Schaltausgänge</h1>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-start">
|
||||
<DigitalOutputsWidget openOutputModal={openOutputModal} />
|
||||
</div>
|
||||
|
||||
{selectedOutput && (
|
||||
<DigitalOutputsModal
|
||||
selectedOutput={selectedOutput}
|
||||
isOpen={isOutputModalOpen}
|
||||
closeOutputModal={closeOutputModal}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigitalOutputsView;
|
||||
173
components/main/kabelueberwachung/KabelueberwachungView.tsx
Normal file
173
components/main/kabelueberwachung/KabelueberwachungView.tsx
Normal file
@@ -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<number>(initialRack); // Nutze initialRack als Startwert
|
||||
const [alarmStatus, setAlarmStatus] = useState<boolean[]>([]); // 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 (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
{[1, 2, 3, 4].map((rack) => (
|
||||
<button
|
||||
key={rack}
|
||||
onClick={() => changeRack(rack)}
|
||||
className={`mr-2 ${
|
||||
Number(activeRack) === Number(rack)
|
||||
? "bg-littwin-blue text-white p-1 rounded-sm"
|
||||
: "bg-gray-300 p-1 text-sm"
|
||||
}`}
|
||||
>
|
||||
Rack {rack}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row space-x-8 xl:space-x-0 2xl:space-x-8 qhd:space-x-16 ml-[5%] mt-[5%]">
|
||||
{(
|
||||
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 (
|
||||
<div key={index} className="flex">
|
||||
<Kue705FO
|
||||
isolationswert={slot.isolationswert}
|
||||
schleifenwiderstand={slot.schleifenwiderstand}
|
||||
modulName={slot.modulName}
|
||||
kueOnline={slot.kueOnlineStatus}
|
||||
alarmStatus={slot.alarmStatus}
|
||||
slotIndex={slotIndex}
|
||||
tdrLocation={slot.tdrLocation}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default KabelueberwachungView;
|
||||
85
components/main/settingsPageComponents/SettingsView.tsx
Normal file
85
components/main/settingsPageComponents/SettingsView.tsx
Normal file
@@ -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 (
|
||||
<div className="p-4">
|
||||
<div className="flex border-b border-gray-200">
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab1"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab1")}
|
||||
>
|
||||
Allgemeine Einstellungen
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab2"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab2")}
|
||||
>
|
||||
OPCUA
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab3"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab3")}
|
||||
>
|
||||
Datenbank
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab4"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab4")}
|
||||
>
|
||||
NTP
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab5"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab5")}
|
||||
>
|
||||
Benutzerverwaltung
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
{activeTab === "tab1" && <GeneralSettings />}
|
||||
{activeTab === "tab2" && <OPCUAInterfaceSettings />}
|
||||
{activeTab === "tab3" && <DatabaseSettings />}
|
||||
{activeTab === "tab4" && <NTPSettings />}
|
||||
{activeTab === "tab5" && <UserManagementSettings />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.562",
|
||||
"version": "1.6.563",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -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 <DashboardView />;
|
||||
|
||||
@@ -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<AppDispatch>();
|
||||
interface DigitalInput {
|
||||
id: number;
|
||||
eingangOffline: boolean;
|
||||
status: boolean;
|
||||
label: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
const [selectedInput, setSelectedInput] = useState<DigitalInput | null>(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 (
|
||||
<div className="flex flex-col gap-3 p-4 h-[calc(100vh-13vh-8vh)] laptop:h-[calc(100vh-10vh-5vh)] xl:h-[calc(100vh-10vh-6vh)] laptop:gap-0">
|
||||
<h1 className="text-base font-semibold mb-2">Meldungseingänge</h1>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-start ">
|
||||
<DigitalInputsWidget
|
||||
openInputModal={openInputModal}
|
||||
inputRange={{ start: 0, end: 16 }}
|
||||
/>
|
||||
<DigitalInputsWidget
|
||||
openInputModal={openInputModal}
|
||||
inputRange={{ start: 16, end: 32 }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{isInputModalOpen && selectedInput && (
|
||||
<InputModal
|
||||
selectedInput={selectedInput}
|
||||
closeInputModal={closeInputModal}
|
||||
isOpen={isInputModalOpen}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigitalInputs;
|
||||
export default function DigitalInputsPage() {
|
||||
return <DigitalInputsView />;
|
||||
}
|
||||
|
||||
@@ -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<AppDispatch>();
|
||||
const [selectedOutput, setSelectedOutput] = useState<DigitalOutput | null>(
|
||||
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 (
|
||||
<div className="flex flex-col gap-3 p-4 h-[calc(100vh-13vh-8vh)] laptop:h-[calc(100vh-10vh-5vh)] xl:h-[calc(100vh-10vh-6vh)] laptop:gap-0">
|
||||
<h1 className="text-base font-semibold mb-2">Schaltausgänge</h1>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-3 gap-4 items-start">
|
||||
<DigitalOutputsWidget openOutputModal={openOutputModal} />
|
||||
</div>
|
||||
|
||||
{selectedOutput && (
|
||||
<DigitalOutputsModal
|
||||
selectedOutput={selectedOutput}
|
||||
isOpen={isOutputModalOpen}
|
||||
closeOutputModal={closeOutputModal}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DigitalOutputs;
|
||||
// pages/digitalOutputs.tsx
|
||||
import DigitalOutputsView from "@/components/main/digitalOutputs/DigitalOutputsView";
|
||||
export default function DigitalOutputsPage() {
|
||||
return <DigitalOutputsView />;
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<div className="p-4">
|
||||
{/* Tab-Navigation */}
|
||||
<div className="flex border-b border-gray-200">
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab1"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab1")}
|
||||
>
|
||||
Allgemeine Einstellungen
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab2"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab2")}
|
||||
>
|
||||
OPCUA
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab3"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab3")}
|
||||
>
|
||||
Datenbank
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab4"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab4")}
|
||||
>
|
||||
NTP
|
||||
</button>
|
||||
<button
|
||||
className={`px-4 py-2 ${
|
||||
activeTab === "tab5"
|
||||
? "border-b-2 border-littwin-blue text-littwin-blue"
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => setActiveTab("tab5")}
|
||||
>
|
||||
Benutzerverwaltung
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Tab-Inhalt */}
|
||||
<div className="mt-4">
|
||||
{activeTab === "tab1" && <GeneralSettings />}
|
||||
{activeTab === "tab2" && <OPCUAInterfaceSettings />}
|
||||
{activeTab === "tab3" && <DatabaseSettings />}
|
||||
{activeTab === "tab4" && <NTPSettings />}
|
||||
{activeTab === "tab5" && <UserManagementSettings />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function EinstellungenPage() {
|
||||
return <SettingsView />;
|
||||
}
|
||||
|
||||
@@ -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<number>(initialRack); // Nutze initialRack als Startwert
|
||||
const [alarmStatus, setAlarmStatus] = useState<boolean[]>([]); // 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 (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
{[1, 2, 3, 4].map((rack) => (
|
||||
<button
|
||||
key={rack}
|
||||
onClick={() => changeRack(rack)}
|
||||
className={`mr-2 ${
|
||||
Number(activeRack) === Number(rack)
|
||||
? "bg-littwin-blue text-white p-1 rounded-sm"
|
||||
: "bg-gray-300 p-1 text-sm"
|
||||
}`}
|
||||
>
|
||||
Rack {rack}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row space-x-8 xl:space-x-0 2xl:space-x-8 qhd:space-x-16 ml-[5%] mt-[5%]">
|
||||
{(
|
||||
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 (
|
||||
<div key={index} className="flex">
|
||||
<Kue705FO
|
||||
isolationswert={slot.isolationswert}
|
||||
schleifenwiderstand={slot.schleifenwiderstand}
|
||||
modulName={slot.modulName}
|
||||
kueOnline={slot.kueOnlineStatus}
|
||||
alarmStatus={slot.alarmStatus}
|
||||
slotIndex={slotIndex}
|
||||
tdrLocation={slot.tdrLocation}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
export default function KabelueberwachungPage() {
|
||||
return <KabelueberwachungView />;
|
||||
}
|
||||
|
||||
export default Kabelueberwachung;
|
||||
|
||||
Reference in New Issue
Block a user