refactorring dashboard
This commit is contained in:
92
components/main/dashboard/Baugruppentraeger.tsx
Normal file
92
components/main/dashboard/Baugruppentraeger.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
"use client"; // components/main/uebersicht/Baugruppentraeger.tsx
|
||||
import React, { useMemo, useEffect } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { RootState, useAppDispatch } from "@/redux/store";
|
||||
import KabelModulStatus from "./modulesStatus/KabelModulStatus";
|
||||
import { getKueDataThunk } from "@/redux/thunks/getKueDataThunk";
|
||||
|
||||
const Baugruppentraeger: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const router = useRouter(); // useRouter für Navigation hinzufügen
|
||||
|
||||
// Redux-Variablen direkt hier abrufen
|
||||
const {
|
||||
kueOnline: kueOnlineRaw,
|
||||
kueVersion,
|
||||
kueCableBreak,
|
||||
kueAlarm1,
|
||||
kueAlarm2,
|
||||
kueGroundFault,
|
||||
} = useSelector((state: RootState) => state.kueDataSlice);
|
||||
|
||||
// `kueOnline` sicherstellen, dass es nur Zahlen enthält
|
||||
const kueOnline = useMemo(
|
||||
() =>
|
||||
kueOnlineRaw.map((value) =>
|
||||
typeof value === "string" ? parseFloat(value) || 0 : value
|
||||
),
|
||||
[kueOnlineRaw]
|
||||
);
|
||||
|
||||
// Klick-Handler für Routing
|
||||
const handleModuleClick = (rackNumber: number) => {
|
||||
router.push(`/kabelueberwachung?rack=${rackNumber}`);
|
||||
};
|
||||
|
||||
const baugruppen: JSX.Element[] = [];
|
||||
const numBaugruppen = Math.ceil(kueOnline.length / 8);
|
||||
|
||||
for (let i = 0; i < numBaugruppen; i++) {
|
||||
const slots = kueOnline.slice(i * 8, (i + 1) * 8);
|
||||
|
||||
baugruppen.push(
|
||||
<div
|
||||
key={i}
|
||||
className="flex bg-white shadow-md rounded-lg mb-4 xl:mb-0 lg:mb-0 border border-gray-200 w-full laptop:scale-y-75 xl:scale-y-90"
|
||||
>
|
||||
<div className="flex gap-1">
|
||||
{slots.map((version, index) => {
|
||||
const slotNumber = i * 8 + index + 1;
|
||||
const isSlotOnline = kueOnline[slotNumber - 1] === 1;
|
||||
const rawModuleVersion = kueVersion[slotNumber - 1] || version;
|
||||
|
||||
// Sicherstellen, dass moduleVersion eine Zahl ist
|
||||
const moduleVersion =
|
||||
typeof rawModuleVersion === "number"
|
||||
? rawModuleVersion
|
||||
: parseFloat(rawModuleVersion) || 0;
|
||||
|
||||
const rackNumber = Math.ceil(slotNumber / 8);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={slotNumber}
|
||||
className="cursor-pointer"
|
||||
onClick={() => handleModuleClick(rackNumber)}
|
||||
>
|
||||
<KabelModulStatus
|
||||
slot={slotNumber}
|
||||
isOnline={isSlotOnline}
|
||||
moduleVersion={moduleVersion}
|
||||
kueCableBreak={kueCableBreak}
|
||||
kueAlarm1={kueAlarm1}
|
||||
kueAlarm2={kueAlarm2}
|
||||
kueGroundFault={kueGroundFault}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
//--------------------------------------------
|
||||
useEffect(() => {
|
||||
dispatch(getKueDataThunk());
|
||||
}, [dispatch]);
|
||||
//--------------------------------------------
|
||||
return <>{baugruppen}</>;
|
||||
};
|
||||
|
||||
export default Baugruppentraeger;
|
||||
105
components/main/dashboard/Last20MessagesTable.tsx
Normal file
105
components/main/dashboard/Last20MessagesTable.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
type Meldung = {
|
||||
t: string; // Zeitstempel
|
||||
s: number; // Status
|
||||
c: string; // Farbe
|
||||
m: string; // Meldung
|
||||
i: string; // Modul/Quelle
|
||||
};
|
||||
|
||||
const Last20MessagesTable: React.FC<{ className?: string }> = ({
|
||||
className,
|
||||
}) => {
|
||||
const [messages, setMessages] = useState<Meldung[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchLast20Messages = async () => {
|
||||
const today = new Date();
|
||||
const prior30 = new Date();
|
||||
prior30.setDate(today.getDate() - 30);
|
||||
|
||||
const format = (d: Date) =>
|
||||
`${d.getFullYear()};${(d.getMonth() + 1)
|
||||
.toString()
|
||||
.padStart(2, "0")};${d.getDate().toString().padStart(2, "0")}`;
|
||||
|
||||
const from = format(prior30);
|
||||
const to = format(today);
|
||||
|
||||
const isDev =
|
||||
typeof window !== "undefined" &&
|
||||
window.location.hostname === "localhost";
|
||||
|
||||
const url = isDev
|
||||
? `/api/cpl/last20MessagesAPIHandler`
|
||||
: `/CPL?Service/ae.ACP&MSS1=${from};${to};All`;
|
||||
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
const raw = await res.json();
|
||||
const data = Array.isArray(raw) ? raw : raw.data;
|
||||
if (!Array.isArray(data)) return;
|
||||
|
||||
const sorted = [...data].sort(
|
||||
(a, b) => new Date(b.t).getTime() - new Date(a.t).getTime()
|
||||
);
|
||||
const last20 = sorted.slice(0, 20); // NEUESTE zuerst
|
||||
|
||||
setMessages(last20);
|
||||
} catch (err) {
|
||||
console.error("Fehler beim Laden der Meldungen:", err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchLast20Messages();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={`bg-white p-1 rounded-lg overflow-auto ${className}`}>
|
||||
<div className="overflow-x-auto overflow-y-auto border rounded shadow-sm h-[95%] pt-1">
|
||||
<table className="min-w-full border">
|
||||
<thead className="bg-gray-100 text-left sticky top-0 z-10">
|
||||
<tr>
|
||||
<th className="p-2 border">Prio</th>
|
||||
<th className="p-2 border">Zeit</th>
|
||||
<th className="p-2 border">Quelle</th>
|
||||
<th className="p-2 border">Meldung</th>
|
||||
<th className="p-2 border">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{messages.length === 0 ? (
|
||||
<tr>
|
||||
<td
|
||||
colSpan={5}
|
||||
className="text-center italic text-gray-500 p-4"
|
||||
>
|
||||
Keine Meldungen verfügbar.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
messages.map((msg, index) => (
|
||||
<tr key={index} className="hover:bg-gray-50">
|
||||
<td className="border p-2">
|
||||
<div
|
||||
className="w-4 h-4 rounded"
|
||||
style={{ backgroundColor: msg.c }}
|
||||
></div>
|
||||
</td>
|
||||
<td className="border p-2 whitespace-nowrap">{msg.t}</td>
|
||||
<td className="border p-2">{msg.i}</td>
|
||||
<td className="border p-2">{msg.m}</td>
|
||||
<td className="border p-2">{msg.s}</td>
|
||||
</tr>
|
||||
))
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Last20MessagesTable;
|
||||
110
components/main/dashboard/NetworkInfo.tsx
Normal file
110
components/main/dashboard/NetworkInfo.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"use client"; //components/main/uebersicht/NetworkInfo.tsx
|
||||
import React, { useEffect } from "react";
|
||||
import Image from "next/image";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RootState, AppDispatch } from "@/redux/store";
|
||||
import { getSystemSettingsThunk } from "@/redux/thunks/getSystemSettingsThunk";
|
||||
import { getOpcUaSettingsThunk } from "@/redux/thunks/getOpcUaSettingsThunk";
|
||||
|
||||
const NetworkInfo: React.FC = () => {
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
|
||||
// ✅ OPC UA Daten laden, wenn Komponente angezeigt wird
|
||||
useEffect(() => {
|
||||
dispatch(getSystemSettingsThunk());
|
||||
dispatch(getOpcUaSettingsThunk());
|
||||
}, [dispatch]);
|
||||
// Werte direkt aus Redux holen
|
||||
const ip =
|
||||
useSelector((state: RootState) => state.systemSettingsSlice.ip) ||
|
||||
"Unbekannt";
|
||||
const subnet =
|
||||
useSelector((state: RootState) => state.systemSettingsSlice.subnet) ||
|
||||
"Unbekannt";
|
||||
const gateway =
|
||||
useSelector((state: RootState) => state.systemSettingsSlice.gateway) ||
|
||||
"Unbekannt";
|
||||
const opcUaZustandRaw = useSelector(
|
||||
(state: RootState) => state.opcuaSettingsSlice.opcUaZustand
|
||||
);
|
||||
|
||||
// OPC-UA Zustand in lesbaren Text umwandeln
|
||||
const opcUaZustand =
|
||||
Number(opcUaZustandRaw) === 1
|
||||
? "Server betriebsbereit"
|
||||
: Number(opcUaZustandRaw) === 0
|
||||
? "Server außer Betrieb"
|
||||
: "Unbekannt";
|
||||
|
||||
return (
|
||||
<div className="w-full flex-direction: row flex">
|
||||
<div className=" flex-grow flex justify-between items-center mt-1 bg-white p-2 rounded-lg shadow-md border border-gray-200 laptop:m-0 laptop:scale-y-75 2xl:scale-y-75">
|
||||
<div className="flex items-center space-x-4">
|
||||
<Image
|
||||
src="/images/IP-icon.svg"
|
||||
alt="IP Address"
|
||||
width={24}
|
||||
height={24}
|
||||
className="w-6 text-littwin-blue"
|
||||
priority
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">IP-Adresse</p>
|
||||
<p className="text-sm font-medium text-gray-700">{ip}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<Image
|
||||
src="/images/subnet-mask.svg"
|
||||
alt="subnet mask"
|
||||
width={24}
|
||||
height={24}
|
||||
className="w-6"
|
||||
priority
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Subnet-Maske</p>
|
||||
<p className="text-sm font-medium text-gray-700">{subnet}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<Image
|
||||
src="/images/gateway.svg"
|
||||
alt="gateway"
|
||||
width={24}
|
||||
height={24}
|
||||
className="w-6"
|
||||
priority
|
||||
/>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Gateway</p>
|
||||
<p className="text-sm font-medium text-gray-700">{gateway}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="text-xs font-bold text-littwin-blue">OPC-UA</div>
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Status</p>
|
||||
<p className="text-sm font-medium text-gray-700">{opcUaZustand}</p>
|
||||
</div>
|
||||
</div>
|
||||
{/* OPC UA Nodeset Name */}
|
||||
{/*
|
||||
<div className="flex items-center space-x-4">
|
||||
<div>
|
||||
<p className="text-xs text-gray-500">Nodeset Name</p>
|
||||
<p className="text-sm font-medium text-gray-700">
|
||||
{opcUaNodesetName}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NetworkInfo;
|
||||
42
components/main/dashboard/VersionInfo.tsx
Normal file
42
components/main/dashboard/VersionInfo.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
"use client"; // components/main/uebersicht/VersionInfo.tsx
|
||||
import React from "react";
|
||||
import { Icon } from "@iconify/react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { RootState } from "../../../redux/store";
|
||||
|
||||
type VersionInfoProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
||||
const appVersion =
|
||||
useSelector((state: RootState) => state.systemSettingsSlice.appVersion) ||
|
||||
"Unbekannt";
|
||||
const webVersion = useSelector(
|
||||
(state: RootState) => state.webVersionSlice.appVersion
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`bg-gray-50 rounded-lg shadow-sm border border-gray-200 w-full laptop:p-2 ${className}`}
|
||||
>
|
||||
<h2 className="text-lg font-semibold text-gray-700 mb-2">
|
||||
Versionsinformationen
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-row p-2 space-x-2">
|
||||
<Icon icon="bx:code-block" className="text-xl text-blue-400" />
|
||||
<p className="text-sm text-gray-600">
|
||||
Applikationsversion: {appVersion}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-row p-2 space-x-2">
|
||||
<Icon icon="mdi:web" className="text-xl text-blue-400" />
|
||||
<p className="text-sm text-gray-600">Webversion: {webVersion}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VersionInfo;
|
||||
15
components/main/dashboard/modulesStatus/Access1Status.tsx
Normal file
15
components/main/dashboard/modulesStatus/Access1Status.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// components/Access1Status.jsx
|
||||
import React from "react";
|
||||
|
||||
const Access1Status = () => {
|
||||
return (
|
||||
<div className="border border-gray-400 w-20 h-10 flex items-center justify-start bg-blue-500">
|
||||
{/* Grüner Streifen auf der linken Seite */}
|
||||
<div className=" left-0 top-0 h-full w-1/6 bg-green-500 mr-2"></div>
|
||||
{/* Blauer Hauptbereich */}
|
||||
<div className="flex flex-row text-white text-xs ">Access 1</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Access1Status;
|
||||
15
components/main/dashboard/modulesStatus/Access2Status.tsx
Normal file
15
components/main/dashboard/modulesStatus/Access2Status.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// components/Access2Status.jsx
|
||||
import React from "react";
|
||||
|
||||
const Access2Status = () => {
|
||||
return (
|
||||
<div className="border border-gray-400 w-20 h-10 flex items-center justify-start bg-blue-500">
|
||||
{/* Grüner Streifen auf der linken Seite */}
|
||||
<div className=" left-0 top-0 h-full w-1/6 bg-green-500 mr-2"></div>
|
||||
{/* Blauer Hauptbereich */}
|
||||
<div className="flex flex-row text-white text-xs ">Access 2</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Access2Status;
|
||||
15
components/main/dashboard/modulesStatus/CPLStatus.tsx
Normal file
15
components/main/dashboard/modulesStatus/CPLStatus.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// components/CPLStatus.jsx
|
||||
import React from "react";
|
||||
|
||||
const CPLStatus = () => {
|
||||
return (
|
||||
<div className="border border-gray-400 w-20 h-10 flex items-center justify-start bg-littwin-blue">
|
||||
{/* Grüner Streifen auf der linken Seite */}
|
||||
<div className=" left-0 top-0 h-full w-1/6 bg-green-500 mr-2"></div>
|
||||
{/* Blauer Hauptbereich */}
|
||||
<div className="text-white text-xs ">CPL</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default CPLStatus;
|
||||
70
components/main/dashboard/modulesStatus/KabelModulStatus.tsx
Normal file
70
components/main/dashboard/modulesStatus/KabelModulStatus.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
const KabelModulStatus: React.FC<KabelModulStatusProps> = ({
|
||||
slot,
|
||||
kueCableBreak,
|
||||
kueAlarm1,
|
||||
kueAlarm2,
|
||||
kueGroundFault,
|
||||
isOnline,
|
||||
moduleVersion,
|
||||
}) => {
|
||||
if (!isOnline) {
|
||||
return (
|
||||
<div className="border border-gray-400 w-10 h-20 flex items-center justify-center bg-gray-200">
|
||||
<div className="text-xs text-gray-500">Leer</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Modultyp basierend auf der Version bestimmen
|
||||
let moduleName = "";
|
||||
let moduleType = "";
|
||||
|
||||
if (moduleVersion === 419) {
|
||||
moduleName = "KÜ705";
|
||||
moduleType = "FO";
|
||||
} else if (moduleVersion === 350) {
|
||||
moduleName = "KÜ605";
|
||||
moduleType = "µC";
|
||||
} else if (moduleVersion === 1100) {
|
||||
moduleName = "KÜSS";
|
||||
moduleType = "___";
|
||||
}
|
||||
|
||||
// Status nur prüfen, wenn der Slot aktiv ist (kueOnline für den Slot ist 1)
|
||||
const isCableBreak =
|
||||
Array.isArray(kueCableBreak) && kueCableBreak[slot - 1] === 1;
|
||||
const isAlarm1 = Array.isArray(kueAlarm1) && kueAlarm1[slot - 1] === 1;
|
||||
const isAlarm2 = Array.isArray(kueAlarm2) && kueAlarm2[slot - 1] === 1;
|
||||
const groundFault =
|
||||
Array.isArray(kueGroundFault) && kueGroundFault[slot - 1] === 1;
|
||||
|
||||
return (
|
||||
<div className="border border-gray-400 w-10 h-20 flex flex-col scale-100 xl:scale-90">
|
||||
<div className="bg-littwin-blue flex-grow flex flex-col items-center justify-center text-white text-[10px]">
|
||||
<div className="flex w-full mb-1 items-start justify-start">{slot}</div>
|
||||
<div className="text-[10px]">{moduleName}</div>
|
||||
<div className="text-[10px]">{moduleType}</div>
|
||||
</div>
|
||||
<div
|
||||
className={`w-full h-2/6 ${
|
||||
isCableBreak || isAlarm1 || isAlarm2 || groundFault
|
||||
? "bg-red-500"
|
||||
: "bg-green-500"
|
||||
}`}
|
||||
></div>
|
||||
<div className="bg-littwin-blue w-full h-1/6"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default KabelModulStatus;
|
||||
|
||||
interface KabelModulStatusProps {
|
||||
slot: number;
|
||||
kueCableBreak: number[];
|
||||
kueAlarm1: number[];
|
||||
kueAlarm2: number[];
|
||||
kueGroundFault: number[];
|
||||
isOnline: boolean;
|
||||
moduleVersion: number;
|
||||
}
|
||||
15
components/main/dashboard/modulesStatus/XioPM1Status.tsx
Normal file
15
components/main/dashboard/modulesStatus/XioPM1Status.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// components/Access1Status.jsx
|
||||
import React from "react";
|
||||
|
||||
const Access1Status = () => {
|
||||
return (
|
||||
<div className="border border-gray-400 w-20 h-10 flex items-center justify-start bg-blue-500">
|
||||
{/* Grüner Streifen auf der linken Seite */}
|
||||
<div className=" left-0 top-0 h-full w-1/6 bg-green-500 mr-2"></div>
|
||||
{/* Blauer Hauptbereich */}
|
||||
<div className="flex flex-row text-white text-xs ">XIOPM 1</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Access1Status;
|
||||
15
components/main/dashboard/modulesStatus/XioPM2Status.tsx
Normal file
15
components/main/dashboard/modulesStatus/XioPM2Status.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
// components/Access1Status.jsx
|
||||
import React from "react";
|
||||
|
||||
const Access1Status = () => {
|
||||
return (
|
||||
<div className="border border-gray-400 w-20 h-10 flex items-center justify-start bg-blue-500">
|
||||
{/* Grüner Streifen auf der linken Seite */}
|
||||
<div className=" left-0 top-0 h-full w-1/6 bg-green-500 mr-2"></div>
|
||||
{/* Blauer Hauptbereich */}
|
||||
<div className="flex flex-row text-white text-xs ">XIOPM 2</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Access1Status;
|
||||
Reference in New Issue
Block a user