git commit -m "feat: UI-Optimierung für analoge Eingänge & Diagramm

- Layout verbessert: Tabelle und Diagramm jetzt nebeneinander (grid-responsive)
- Responsivität optimiert: Kein unnötiges horizontales Scrollen mehr
- Diagramm verbessert:
  - Höhere Lesbarkeit mit stärkeren Linien und Abstand für Achsenbeschriftungen
  - Anpassung der Höhe und Breite für mobile & große Bildschirme
- Tabelle optimiert:
  - Bessere Lesbarkeit mit größeren Abständen
  - Hover-Effekte für Zeilen
  - Dynamisches Spaltenlayout für kleine & große Bildschirme
- Code aufgeräumt und verbessert

Test auf verschiedenen Bildschirmgrößen erfolgreich!"
This commit is contained in:
Ismail Ali
2025-02-25 19:46:52 +01:00
parent 327a101386
commit 8c01dd3fc3
12 changed files with 591 additions and 175 deletions

View File

@@ -0,0 +1,50 @@
"use client"; // components/main/analogeEingaenge/AnalogInputsChart.tsx
import React from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../../redux/store";
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from "recharts";
export default function AnalogInputsChart() {
const analogInputs = useSelector(
(state: RootState) => state.analogeEingaenge
);
// Daten für das Diagramm vorbereiten
const chartData = Object.values(analogInputs).map((input) => ({
name: `Input ${input.id}`,
value: input.value ?? 0, // Falls kein Wert vorhanden ist, wird 0 gesetzt
}));
return (
<div className="w-full h-[350px] bg-white shadow-md rounded-lg p-4 border border-gray-200">
<h3 className="text-sm font-semibold mb-2 text-gray-700">
Analog Inputs Chart
</h3>
<ResponsiveContainer width="100%" height="100%">
<LineChart
data={chartData}
margin={{ top: 10, right: 20, left: 0, bottom: 10 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Line
type="monotone"
dataKey="value"
stroke="#4A90E2"
strokeWidth={2}
/>
</LineChart>
</ResponsiveContainer>
</div>
);
}

View File

@@ -1,108 +0,0 @@
"use client"; // components/modules/AnalogeEingaengeComponent.tsx
import React, { useState } from "react";
import { Icon } from "@iconify/react";
import { analogInputs } from "../../../data/mockdata/analogInputs";
const AnalogeEingaengeComponent = () => {
const [activeConfig, setActiveConfig] = useState<number | null>(null);
return (
<div className="border rounded-lg shadow-md bg-white flex flex-col h-full p-4 lg:p-6 xl:p-8">
<h3 className="text-sm lg:text-base xl:text-lg font-semibold mb-2">
Analoge Eingänge
</h3>
<div className="overflow-auto flex-grow">
<table className="table-auto w-full text-xs lg:text-sm xl:text-md text-left border-collapse">
<thead className="bg-gray-100 text-gray-700">
<tr>
<th className="px-2 lg:px-3 py-1 lg:py-2 whitespace-nowrap">
Eingang
</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 whitespace-nowrap">
Wert
</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 whitespace-nowrap">
Bezeichnung
</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 text-center">uW</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 text-center">uG</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 text-center">oW</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 text-center">oG</th>
<th className="px-2 lg:px-3 py-1 lg:py-2 text-center">Aktion</th>
</tr>
</thead>
<tbody className="text-gray-600">
{analogInputs.map((input) => (
<tr
key={input.id}
className="border-t hover:bg-gray-50 transition"
>
<td className="px-2 lg:px-3 py-1 lg:py-2">{input.id}</td>
<td className="px-2 lg:px-3 py-1 lg:py-2">{input.value}</td>
<td className="px-2 lg:px-3 py-1 lg:py-2">{input.name}</td>
<td className="px-2 lg:px-3 py-1 lg:py-2 text-center">
<span
className={
input.uW
? "text-green-500 text-lg lg:text-xl"
: "text-gray-400"
}
>
</span>
</td>
<td className="px-2 lg:px-3 py-1 lg:py-2 text-center">
<span
className={
input.uG
? "text-green-500 text-lg lg:text-xl"
: "text-gray-400"
}
>
</span>
</td>
<td className="px-2 lg:px-3 py-1 lg:py-2 text-center">
<span
className={
input.oW
? "text-orange-500 text-lg lg:text-xl"
: "text-gray-400"
}
>
</span>
</td>
<td className="px-2 lg:px-3 py-1 lg:py-2 text-center">
<span
className={
input.oG
? "text-green-500 text-lg lg:text-xl"
: "text-gray-400"
}
>
</span>
</td>
<td className="px-2 lg:px-3 py-1 lg:py-2 text-center">
<button
onClick={() => setActiveConfig(input.id)}
className="text-littwin-blue hover:text-littwin-dark transition"
>
<Icon
icon="mdi:cog-outline"
className="w-4 lg:w-5 xl:w-6"
/>
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
};
export default AnalogeEingaengeComponent;

View File

@@ -0,0 +1,66 @@
"use client"; // components/main/analogeEingaenge/AnalogeEingaengeTable.tsx
import React from "react";
import { useSelector } from "react-redux";
import { RootState } from "../../../redux/store";
import { useFetchAnalogeEingaenge } from "./hooks/useFetchAnalogeEingaenge"; // ✅ Hook importieren
export default function AnalogeEingaengeTable() {
useFetchAnalogeEingaenge(); // ✅ Hook aufrufen (lädt Daten und aktualisiert sie regelmäßig)
const analogeEingaenge = useSelector(
(state: RootState) => state.analogeEingaenge
);
return (
<div className="w-full">
{/* Card Container */}
<div className="bg-white shadow-lg rounded-lg p-4 border border-gray-200">
{/* Card Title */}
<h2 className="text-lg md:text-xl font-semibold mb-4 text-gray-700">
Analoge Eingänge
</h2>
{/* Tabelle in einem Scroll-Container für kleine Screens */}
<div className="overflow-x-auto">
<table className="w-full border-collapse border border-gray-300 text-sm md:text-base">
<thead>
<tr className="bg-gray-100 text-gray-700">
<th className="border p-3 text-left">Eingang</th>
<th className="border p-3 text-left">Wert</th>
<th className="border p-3 text-left">Bezeichnung</th>
<th className="border p-3 text-center">uW</th>
<th className="border p-3 text-center">uG</th>
<th className="border p-3 text-center">oW</th>
<th className="border p-3 text-center">oG</th>
</tr>
</thead>
<tbody>
{Object.values(analogeEingaenge).map((eingang, index) => (
<tr
key={index}
className="text-gray-700 hover:bg-gray-50 transition"
>
<td className="border p-3">{eingang.id ?? "-"}</td>
<td className="border p-3">{eingang.value ?? "-"}</td>
<td className="border p-3">{eingang.name || "----"}</td>
<td className="border p-3 text-center">
{eingang.uW ? "🟢" : "⚪"}
</td>
<td className="border p-3 text-center">
{eingang.uG ? "🟢" : "⚪"}
</td>
<td className="border p-3 text-center">
{eingang.oW ? "🟠" : "⚪"}
</td>
<td className="border p-3 text-center">
{eingang.oG ? "🟢" : "⚪"}
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,28 @@
// components/main/analogeEingaenge/hooks/useFetchAnalogeEingaenge.ts
import { useEffect } from "react";
import { useDispatch } from "react-redux";
import {
loadFromWindow,
setAnalogeEingaenge,
} from "../../../../redux/slices/analogeEingaengeSlice";
export const useFetchAnalogeEingaenge = () => {
const dispatch = useDispatch();
useEffect(() => {
dispatch(loadFromWindow()); // Initial Mock-Daten aus `window` laden
const interval = setInterval(async () => {
try {
const response = await fetch("/api/get-embedded-data");
if (!response.ok) throw new Error("Fehler beim Abrufen der Daten");
const data = await response.json();
dispatch(setAnalogeEingaenge(data));
} catch (error) {
console.error("Fehler beim Abruf der Sensordaten:", error);
}
}, 5000); // Alle 5 Sekunden neue Daten abrufen
return () => clearInterval(interval);
}, [dispatch]);
};

View File

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

300
package-lock.json generated
View File

@@ -36,6 +36,7 @@
"react-redux": "^9.1.2",
"react-spinners": "^0.14.1",
"react-toastify": "^10.0.6",
"recharts": "^2.15.1",
"redux": "^5.0.1",
"redux-persist": "^6.0.0"
},
@@ -529,7 +530,6 @@
"version": "7.26.7",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
"dev": true,
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
@@ -1722,6 +1722,60 @@
"cypress": "*"
}
},
"node_modules/@types/d3-array": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
"integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.7",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
"integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="
},
"node_modules/@types/graceful-fs": {
"version": "4.1.9",
"resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -3174,8 +3228,7 @@
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"devOptional": true
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/cypress": {
"version": "14.0.0",
@@ -3327,6 +3380,116 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz",
"integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"engines": {
"node": ">=12"
}
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
@@ -3391,6 +3554,11 @@
"integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
"dev": true
},
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="
},
"node_modules/dedent": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
@@ -3486,6 +3654,15 @@
"dev": true,
"peer": true
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/domexception": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
@@ -3744,6 +3921,11 @@
"integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==",
"dev": true
},
"node_modules/eventemitter3": {
"version": "4.0.7",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
"integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="
},
"node_modules/execa": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
@@ -3865,6 +4047,14 @@
"node >=0.6.0"
]
},
"node_modules/fast-equals": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz",
"integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/fast-glob": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
@@ -4515,6 +4705,14 @@
"node": ">=10"
}
},
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"engines": {
"node": ">=12"
}
},
"node_modules/is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@@ -5822,8 +6020,7 @@
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lodash.includes": {
"version": "4.3.0",
@@ -7127,6 +7324,20 @@
}
}
},
"node_modules/react-smooth": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
"integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==",
"dependencies": {
"fast-equals": "^5.0.1",
"prop-types": "^15.8.1",
"react-transition-group": "^4.4.5"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/react-spinners": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.14.1.tgz",
@@ -7148,6 +7359,21 @@
"react-dom": ">=18"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -7169,6 +7395,41 @@
"node": ">=8.10.0"
}
},
"node_modules/recharts": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.1.tgz",
"integrity": "sha512-v8PUTUlyiDe56qUj82w/EDVuzEFXwEHp9/xOowGAZwfLjB9uAy3GllQVIYMWF6nU+qibx85WF75zD7AjqoT54Q==",
"dependencies": {
"clsx": "^2.0.0",
"eventemitter3": "^4.0.1",
"lodash": "^4.17.21",
"react-is": "^18.3.1",
"react-smooth": "^4.0.4",
"recharts-scale": "^0.4.4",
"tiny-invariant": "^1.3.1",
"victory-vendor": "^36.6.8"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/recharts-scale": {
"version": "0.4.5",
"resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz",
"integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==",
"dependencies": {
"decimal.js-light": "^2.4.1"
}
},
"node_modules/recharts/node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
},
"node_modules/redent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -7218,8 +7479,7 @@
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"dev": true
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/request-progress": {
"version": "3.0.0",
@@ -8046,6 +8306,11 @@
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
"dev": true
},
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="
},
"node_modules/tldts": {
"version": "6.1.75",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz",
@@ -8415,6 +8680,27 @@
"extsprintf": "^1.2.0"
}
},
"node_modules/victory-vendor": {
"version": "36.9.2",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz",
"integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/w3c-xmlserializer": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",

View File

@@ -41,6 +41,7 @@
"react-redux": "^9.1.2",
"react-spinners": "^0.14.1",
"react-toastify": "^10.0.6",
"recharts": "^2.15.1",
"redux": "^5.0.1",
"redux-persist": "^6.0.0"
},

View File

@@ -1,37 +1,32 @@
"use client"; ///pages/analogeEingaenge.tsx
import React, { useState } from "react";
import AnalogeEingaengeComponent from "../components/main/analogeEingaenge/AnalogeEingaengeComponent";
import XioPM from "../components/main/analogeEingaenge/XioPM";
import { analogInputs } from "../data/mockdata/analogInputs";
import { xioPm1Inputs } from "../data/mockdata/xioPm1Inputs";
import { xioPm2Inputs } from "../data/mockdata/xioPm2Inputs";
import AnalogeEingaengeTabelle from "../components/main/analogeEingaenge/AnalogeEingaengeTable";
import AnalogInputsChart from "../components/main/analogeEingaenge/AnalogInputsChart";
function AnalogeEingaenge() {
const [activeConfig, setActiveConfig] = useState<number | null>(null);
return (
<div className="flex flex-col h-[calc(100vh-13vh-8vh)] laptop:h-[calc(100vh-10vh-6vh)] xl:h-[calc(100vh-10vh-6vh)] bg-gray-100">
<div className="flex-grow grid grid-cols-2 gap-1 laptop:gap-2 2xl:p-4 laptop:p-1 overflow-auto">
<AnalogeEingaengeComponent />
<div className="min-h-screen bg-gray-100 p-4">
<div className="container mx-auto">
{/* Responsive Grid: 1 Spalte auf mobilen Geräten, 2 Spalten auf größeren Geräten */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Tabelle als Card */}
<div className="bg-white shadow-lg rounded-lg p-4 border border-gray-200">
<h2 className="text-xl font-semibold mb-4 text-gray-700">
Analoge Eingänge
</h2>
<AnalogeEingaengeTabelle />
</div>
<div className="border rounded-lg shadow-md p-2 bg-white laptop:p-4">
<h3 className="text-sm font-semibold mb-1">Diagramm</h3>
<p>Diagramm wird hier eingefügt</p>
{/* Diagramm als Card */}
<div className="bg-white shadow-lg rounded-lg p-4 border border-gray-200">
<h2 className="text-xl font-semibold mb-4 text-gray-700">
Analog Inputs Chart
</h2>
<AnalogInputsChart />
</div>
</div>
{/*
<XioPM
title="XIO-PM 1"
data={xioPm1Inputs}
onConfigClick={(id) => setActiveConfig(id)}
/>
<XioPM
title="XIO-PM 2"
data={xioPm2Inputs}
onConfigClick={(id) => setActiveConfig(id)}
/>
*/}
</div>
</div>
);

View File

@@ -0,0 +1,19 @@
// public/CPLmockData/SERVICE/ae.js
var win_analogeEingaenge1 = [1, 0, "----", 1, 1, 0, 1]; // Eingang 1
var win_analogeEingaenge2 = [2, 22.91, "Feuchtigkeit", 1, 1, 1, 0]; // Eingang 2
var win_analogeEingaenge3 = [3, 0, "----", 1, 1, 0, 1]; // Eingang 3
var win_analogeEingaenge4 = [4, 0, "----", 1, 1, 0, 1]; // Eingang 4
var win_analogeEingaenge5 = [5, 0, "----", 1, 1, 0, 1]; // Eingang 5
var win_analogeEingaenge6 = [6, 21, "Temperatur", 1, 1, 0, 1]; // Eingang 6
var win_analogeEingaenge7 = [7, 0, "----", 1, 1, 1, 0]; // Eingang 7
var win_analogeEingaenge8 = [8, 0, "----", 1, 1, 0, 1]; // Eingang 8
/*
ID (z. B. 1, 2, ... 8) → Identifikation des Eingangs
Wert (z. B. 0, 22.91, 21) → Der analoge Wert
Bezeichnung (z. B. "----", "Feuchtigkeit", "Temperatur") → Name des Sensors
uW (Unterer Warnwert) → 1 = grün, 0 = grau
uG (Unterer Grenzwert) → 1 = grün, 0 = grau
oW (Oberer Warnwert) → 1 = orange, 0 = grau
oG (Oberer Grenzwert) → 1 = grün, 0 = grau
*/

View File

@@ -0,0 +1,110 @@
// redux/slices/analogeEingaengeSlice.ts
import { createSlice, PayloadAction, createAsyncThunk } from "@reduxjs/toolkit";
// Typ für einen einzelnen analogen Eingang
export interface AnalogerEingang {
id: number | null;
value: number | null;
name: string;
uW: boolean;
uG: boolean;
oW: boolean;
oG: boolean;
}
// Typ für den Gesamt-State
export interface AnalogeEingaengeState {
win_analogeEingaenge1: AnalogerEingang;
win_analogeEingaenge2: AnalogerEingang;
win_analogeEingaenge3: AnalogerEingang;
win_analogeEingaenge4: AnalogerEingang;
win_analogeEingaenge5: AnalogerEingang;
win_analogeEingaenge6: AnalogerEingang;
win_analogeEingaenge7: AnalogerEingang;
win_analogeEingaenge8: AnalogerEingang;
}
// Standardwert für einen Eingang
const defaultAnalogerEingang: AnalogerEingang = {
id: null,
value: null,
name: "",
uW: false,
uG: false,
oW: false,
oG: false,
};
// Initialer Zustand mit leeren Werten
const initialState: AnalogeEingaengeState = {
win_analogeEingaenge1: { ...defaultAnalogerEingang },
win_analogeEingaenge2: { ...defaultAnalogerEingang },
win_analogeEingaenge3: { ...defaultAnalogerEingang },
win_analogeEingaenge4: { ...defaultAnalogerEingang },
win_analogeEingaenge5: { ...defaultAnalogerEingang },
win_analogeEingaenge6: { ...defaultAnalogerEingang },
win_analogeEingaenge7: { ...defaultAnalogerEingang },
win_analogeEingaenge8: { ...defaultAnalogerEingang },
};
// Mock-Daten aus `window` laden
export const loadFromWindow = createAsyncThunk(
"analogeEingaenge/loadFromWindow",
async () => {
const entries = Object.entries(window).filter(([key]) =>
key.startsWith("win_analogeEingaenge")
);
const data: Partial<AnalogeEingaengeState> = {};
entries.forEach(([key, value]) => {
if (Array.isArray(value) && value.length === 7) {
data[key as keyof AnalogeEingaengeState] = {
id: value[0],
value: value[1],
name: value[2],
uW: value[3] === 1,
uG: value[4] === 1,
oW: value[5] === 1,
oG: value[6] === 1,
};
}
});
return data;
}
);
// Redux Slice
const analogeEingaengeSlice = createSlice({
name: "analogeEingaenge",
initialState,
reducers: {
setAnalogerEingang(
state,
action: PayloadAction<{
key: keyof AnalogeEingaengeState;
value: AnalogerEingang;
}>
) {
state[action.payload.key] = action.payload.value;
},
resetAnalogeEingaenge(state) {
Object.keys(state).forEach((key) => {
state[key as keyof AnalogeEingaengeState] = {
...defaultAnalogerEingang,
};
});
},
},
extraReducers: (builder) => {
builder.addCase(loadFromWindow.fulfilled, (state, action) => {
Object.assign(state, action.payload);
});
},
});
export const { setAnalogerEingang, resetAnalogeEingaenge } =
analogeEingaengeSlice.actions;
export default analogeEingaengeSlice.reducer;

View File

@@ -1,12 +1,8 @@
// redux/slices/variablesSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { DataTDR } from "../types/chartDataTypesTDR";
// Typ für den State
export interface VariablesState {
selectedChartData: DataTDR[] | null;
selectedFileName: string | null;
//------------
kueBezeichnungen: string[];
isolationsgrenzwerte: number[];
verzoegerung: number[];
@@ -40,20 +36,10 @@ export interface VariablesState {
kueOverflow: number[] | null;
tdrLast: string | null;
appVersion: string | null;
win_analogeEingaenge1: string | null;
win_analogeEingaenge2: string | null;
win_analogeEingaenge3: string | null;
win_analogeEingaenge4: string | null;
win_analogeEingaenge5: string | null;
win_analogeEingaenge6: string | null;
win_analogeEingaenge7: string | null;
win_analogeEingaenge8: string | null;
}
// Initialer Zustand
const initialState: VariablesState = {
selectedFileName: null,
selectedChartData: null,
kueBezeichnungen: [],
isolationsgrenzwerte: [],
verzoegerung: [],
@@ -86,14 +72,6 @@ const initialState: VariablesState = {
kueOverflow: null,
tdrLast: null,
appVersion: null,
win_analogeEingaenge1: null,
win_analogeEingaenge2: null,
win_analogeEingaenge3: null,
win_analogeEingaenge4: null,
win_analogeEingaenge5: null,
win_analogeEingaenge6: null,
win_analogeEingaenge7: null,
win_analogeEingaenge8: null,
};
// Slice erstellen
@@ -118,20 +96,9 @@ const variablesSlice = createSlice({
] as VariablesState[keyof VariablesState]) = value!;
});
},
setSelectedChartData(state, action: PayloadAction<DataTDR[] | null>) {
state.selectedChartData = action.payload;
},
setSelectedFileName(state, action: PayloadAction<string | null>) {
state.selectedFileName = action.payload;
},
},
});
export const {
setVariable,
setVariables,
setSelectedChartData,
setSelectedFileName,
} = variablesSlice.actions;
export const { setVariable, setVariables } = variablesSlice.actions;
export default variablesSlice.reducer;

View File

@@ -10,6 +10,7 @@ import dashboardReducer from "./slices/dashboardSlice";
import systemSettingsReducer from "./slices/systemSettingsSlice";
import opcuaSettingsReducer from "./slices/opcuaSettingsSlice";
import digitalOutputsReducer from "./slices/digitalOutputsSlice";
import analogeEingaengeReducer from "./slices/analogeEingaengeSlice";
const store = configureStore({
reducer: {
@@ -23,6 +24,7 @@ const store = configureStore({
systemSettings: systemSettingsReducer,
opcuaSettings: opcuaSettingsReducer,
digitalOutputs: digitalOutputsReducer,
analogeEingaenge: analogeEingaengeReducer,
},
});