feat: dynamische URL-Anpassung für Entwicklungs- und Produktionsumgebung

- Navigation und Weiterleitungen angepasst, um dynamisch `.html`-Endungen in Produktionsumgebung anzuhängen.
- Nutzung von `NEXT_PUBLIC_NODE_ENV` ermöglicht unterschiedliche URL-Strukturen in Entwicklungs- und Produktionsumgebung.
- `Navigation`-Komponente und `index.js` entsprechend konfiguriert, um `.html` in der Produktionsumgebung automatisch anzuhängen.
- Verbesserte Konsistenz und Funktionalität zwischen beiden Umgebungen, 404-Fehler in Produktion behoben.
This commit is contained in:
ISA
2024-10-25 11:21:56 +02:00
parent 655a86e915
commit ab8a80f34c
21 changed files with 108 additions and 110 deletions

34
pages/_app.js Normal file
View File

@@ -0,0 +1,34 @@
import { useEffect } from "react";
import Header from "../components/Header";
import Navigation from "../components/Navigation";
import Footer from "../components/Footer";
import "../styles/globals.css";
function MyApp({ Component, pageProps }) {
// Client-seitige Logik kann hier hinzugefügt werden
useEffect(() => {
if (typeof window !== "undefined") {
console.log("Client-side logic can run here.");
}
}, []);
return (
<div className="bg-gray-100 flex flex-col min-h-screen overflow-hidden">
<Header />
<div className="flex flex-grow w-full">
{" "}
{/* Flexbox-Container für Navigation und Content */}
<Navigation className="w-1/5" />{" "}
{/* Navigation nimmt 20% der Breite ein */}
<main className="flex-1 p-4">
{" "}
{/* Der Content nimmt den restlichen Platz ein */}
<Component {...pageProps} />
</main>
</div>
<Footer />
</div>
);
}
export default MyApp;

16
pages/_document.js Normal file
View File

@@ -0,0 +1,16 @@
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html lang="de">
<Head>
{/* Füge Meta-Tags, CSS-Links und andere Header-Inhalte hier hinzu */}
<link rel="icon" href="/favicon.png" type="image/png" />
</Head>
<body>
<Main /> {/* Hier wird der Seiteninhalt eingebettet */}
<NextScript /> {/* Fügt Next.js-Skripte für die Seite hinzu */}
</body>
</Html>
);
}

133
pages/access.js Normal file
View File

@@ -0,0 +1,133 @@
"use client";
import React from "react";
function Access() {
return (
<div className="bg-gray-100 min-h-screen p-8">
<div className="bg-white rounded-lg shadow p-6 mb-8">
<h2 className="text-xl font-bold text-blue-500 mb-4">
Zutrittskontrolle 1
</h2>
<div className="flex justify-between">
<div className="w-full lg:w-1/2 p-4">
<table className="min-w-full text-sm">
<tbody>
<tr>
<td className="py-3 px-4">
<b>Betrieb</b>
</td>
<td className="py-3 px-4">Status: ...</td>
</tr>
<tr>
<td className="py-3 px-4">
<b>Status</b>
</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Letzte Chip-ID</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Zeitstempel</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Access Typ</td>
<td className="py-3 px-4">...</td>
</tr>
</tbody>
</table>
</div>
<div className="w-full lg:w-1/2 p-4">
<table className="min-w-full text-sm">
<thead className="bg-gray-200">
<tr>
<th className="py-3 px-4 text-center">Eingang</th>
<th className="py-3 px-4 text-center">Zustand</th>
<th className="py-3 px-4">Bezeichnung</th>
</tr>
</thead>
<tbody>
<tr>
<td className="py-3 px-4 text-center">1</td>
<td className="py-3 px-4 text-center">...</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4 text-center">2</td>
<td className="py-3 px-4 text-center">...</td>
<td className="py-3 px-4">...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
{/* Zutrittskontrolle 2 */}
<div className="bg-white rounded-lg shadow p-6">
<h2 className="text-xl font-bold text-blue-500 mb-4">
Zutrittskontrolle 2
</h2>
<div className="flex justify-between">
<div className="w-full lg:w-1/2 p-4">
<table className="min-w-full text-sm">
<tbody>
<tr>
<td className="py-3 px-4">
<b>Betrieb</b>
</td>
<td className="py-3 px-4">Status: ...</td>
</tr>
<tr>
<td className="py-3 px-4">
<b>Status</b>
</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Letzte Chip-ID</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Zeitstempel</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4">Access Typ</td>
<td className="py-3 px-4">...</td>
</tr>
</tbody>
</table>
</div>
<div className="w-full lg:w-1/2 p-4">
<table className="min-w-full text-sm">
<thead className="bg-gray-200">
<tr>
<th className="py-3 px-4 text-center">Eingang</th>
<th className="py-3 px-4 text-center">Zustand</th>
<th className="py-3 px-4">Bezeichnung</th>
</tr>
</thead>
<tbody>
<tr>
<td className="py-3 px-4 text-center">1</td>
<td className="py-3 px-4 text-center">...</td>
<td className="py-3 px-4">...</td>
</tr>
<tr>
<td className="py-3 px-4 text-center">2</td>
<td className="py-3 px-4 text-center">...</td>
<td className="py-3 px-4">...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
);
}
export default Access;

222
pages/analogeEingaenge.js Normal file
View File

@@ -0,0 +1,222 @@
"use client";
import React, { useState } from "react";
function AnalogeEingaenge() {
const [activeConfig, setActiveConfig] = useState(null);
// Beispiel-Daten, du kannst die Daten dynamisch laden
const inputs = [
{
id: 1,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 2,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 3,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 4,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 5,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 6,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 7,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
{
id: 8,
value: 0,
name: "------------------",
uG: true,
uW: true,
oW: true,
oG: true,
},
];
return (
<div className="bg-gray-100 min-h-screen p-8">
<h1 className="text-2xl font-bold mb-4">Analoge Eingänge</h1>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{inputs.map((input) => (
<div key={input.id} className="bg-white rounded-lg shadow-lg p-6">
<div className="flex justify-between items-center bg-blue-500 text-white rounded-t-lg p-4">
<h2 className="text-lg">XIO-PM {input.id}</h2>
<button
className="bg-green-500 hover:bg-green-600 text-white py-2 px-4 rounded"
onClick={() => setActiveConfig(input.id)}
>
Online
</button>
</div>
<div className="p-4">
<table className="min-w-full bg-white border border-gray-300">
<thead>
<tr className="bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th className="py-3 px-4 text-center">Eingang</th>
<th className="py-3 px-4 text-center">Wert</th>
<th className="py-3 px-4 text-left">Bezeichnung</th>
<th className="py-3 px-4 text-center">uG</th>
<th className="py-3 px-4 text-center">uW</th>
<th className="py-3 px-4 text-center">oW</th>
<th className="py-3 px-4 text-center">oG</th>
<th className="py-3 px-4 text-center">Konfig</th>
</tr>
</thead>
<tbody className="text-gray-600 text-sm">
<tr className="border-b border-gray-200">
<td className="py-3 px-4 text-center">{input.id}</td>
<td className="py-3 px-4 text-right">{input.value}</td>
<td className="py-3 px-4">{input.name}</td>
<td className="py-3 px-4 text-center">
<i
className={`bi bi-circle-fill text-${
input.uG ? "green" : "gray"
}-500`}
></i>
</td>
<td className="py-3 px-4 text-center">
<i
className={`bi bi-circle-fill text-${
input.uW ? "green" : "gray"
}-500`}
></i>
</td>
<td className="py-3 px-4 text-center">
<i
className={`bi bi-circle-fill text-${
input.oW ? "green" : "gray"
}-500`}
></i>
</td>
<td className="py-3 px-4 text-center">
<i
className={`bi bi-circle-fill text-${
input.oG ? "green" : "gray"
}-500`}
></i>
</td>
<td className="py-3 px-4 text-center">
<button
className="text-blue-500"
onClick={() => setActiveConfig(input.id)}
>
<i className="bi bi-gear-fill"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
))}
</div>
{/* Modal für Konfiguration */}
{activeConfig && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white p-8 rounded-lg shadow-lg w-96">
<h2 className="text-xl font-bold mb-4">
Konfiguration - XIO-PM {activeConfig}
</h2>
<button
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
onClick={() => setActiveConfig(null)}
>
<i className="bi bi-x-lg"></i>
</button>
{/* Konfigurationsformular hier einfügen */}
<form>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="bezeichnung"
>
Bezeichnung
</label>
<input
type="text"
id="bezeichnung"
name="bezeichnung"
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder="Bezeichnung eingeben"
/>
</div>
<div className="mb-4">
<label
className="block text-gray-700 text-sm font-bold mb-2"
htmlFor="ug"
>
Unterer Grenzwert
</label>
<input
type="number"
id="ug"
name="ug"
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
placeholder="Wert eingeben"
/>
</div>
{/* Weitere Felder hier hinzufügen */}
<button
type="button"
className="bg-blue-500 text-white py-2 px-4 rounded hover:bg-blue-600"
onClick={() => setActiveConfig(null)}
>
Speichern
</button>
</form>
</div>
</div>
)}
</div>
);
}
export default AnalogeEingaenge;

371
pages/dashboard.js Normal file
View File

@@ -0,0 +1,371 @@
"use client"; // app/dashboard/page.jsx
import React, { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import "tailwindcss/tailwind.css";
import "@fontsource/roboto";
import "bootstrap-icons/font/bootstrap-icons.css";
import { loadWindowVariables } from "../utils/loadWindowVariables";
import CPLStatus from "../components/modulesStatus/CPLStatus";
import Access1Status from "../components/modulesStatus/Access1Status";
import Access2Status from "../components/modulesStatus/Access2Status";
import KabelModulStatus from "../components/modulesStatus/KabelModulStatus";
import XioPM1Status from "../components/modulesStatus/XioPM1Status";
import XioPM2Status from "../components/modulesStatus/XioPM2Status";
import { Icon } from "@iconify/react";
function Dashboard() {
const [isClient, setIsClient] = useState(false);
const router = useRouter(); // Router instanzieren
const [last20Messages, setLast20Messages] = useState([]);
const [kueOnline, setkueOnline] = useState([]);
const [ip, setIp] = useState("");
const [subnet, setSubnet] = useState("");
const [gateway, setGateway] = useState("");
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [kueCableBreak, setKueCableBreak] = useState([]);
const [appVersion, setAppVersion] = useState("");
useEffect(() => {
if (typeof window !== "undefined") {
loadWindowVariables()
.then(() => {
if (window.last20Messages) {
const parsedMessages = parseMessages(window.last20Messages);
setLast20Messages(parsedMessages);
setIp(window.ip);
setSubnet(window.subnet);
setGateway(window.gateway);
setAppVersion(window.appVersion);
} else {
console.error("Konnte last20Messages nicht finden.");
setError("Konnte last20Messages nicht finden.");
}
setLoading(false);
})
.catch((error) => {
console.error("Fehler beim Laden des Skripts:", error);
setError(error);
setLoading(false);
});
}
}, []);
useEffect(() => {
loadWindowVariables()
.then(() => {
if (window.kueCableBreak && Array.isArray(window.kueCableBreak)) {
// Prüfe, ob window.kueCableBreak ein Array ist
const cableBreakArray = window.kueCableBreak.map(Number);
setKueCableBreak(cableBreakArray); // Array für kueCableBreak
} else if (typeof window.kueCableBreak === "string") {
// Falls es ein String ist, splitte den String und mappe ihn in ein Array
const cableBreakArray = window.kueCableBreak.split(",").map(Number);
setKueCableBreak(cableBreakArray);
} else {
console.error(
"Konnte kueCableBreak nicht finden oder es ist kein gültiges Array/String."
);
setError(
"Konnte kueCableBreak nicht finden oder es ist kein gültiges Array/String."
);
}
setLoading(false);
})
.catch((error) => {
console.error("Fehler beim Laden des Skripts:", error);
setError(error);
setLoading(false);
});
}, []);
const parseMessages = (messages) => {
messages = messages
.replace(/<tr>/g, "\n")
.replace(/<\/?td>/g, "")
.replace(/<\/tr>/g, "")
.trim();
const rows = messages.split("\n");
return rows.map((row) => {
const columns = [
row.substring(0, 5), // ID
row.substring(5, 10), // Wert (z.B. Modulnummer)
row.substring(10, 29), // Zeitstempel, Millisekunden entfernt :000
row.substring(33, row.length - 1), // Meldung (ohne letztes Zeichen)
row.substring(row.length - 1), // Status (letztes Zeichen)
];
return columns;
});
};
useEffect(() => {
loadWindowVariables()
.then(() => {
// console.log("kueOnline Data: ", window.kueOnline); // Debug: Ausgabe von kueOnline
if (window.kueOnline) {
if (Array.isArray(window.kueOnline)) {
const versionArray = window.kueOnline.map(Number);
setkueOnline(versionArray);
} else {
console.error("kueOnline ist kein Array:", window.kueOnline);
setError("Konnte kueOnline nicht als Array verarbeiten.");
}
} else {
console.error("Konnte kueOnline nicht finden.");
setError("Konnte kueOnline nicht finden.");
}
setLoading(false);
})
.catch((error) => {
console.error("Fehler beim Laden des Skripts:", error);
setError(error);
setLoading(false);
});
}, []);
useEffect(() => {
loadWindowVariables()
.then(() => {
// Debug-Ausgaben für kueAlarm1 und kueAlarm2
//console.log("kueAlarm1 Data: ", window.kueAlarm1);
//console.log("kueAlarm2 Data: ", window.kueAlarm2);
if (window.kueAlarm1 && Array.isArray(window.kueAlarm1)) {
//console.log("kueAlarm1 ist ein Array:", window.kueAlarm1);
} else {
console.error("kueAlarm1 ist kein Array oder nicht definiert.");
setError(
"Konnte kueAlarm1 nicht finden oder es ist kein gültiges Array."
);
}
if (window.kueAlarm2 && Array.isArray(window.kueAlarm2)) {
//console.log("kueAlarm2 ist ein Array:", window.kueAlarm2);
} else {
console.error("kueAlarm2 ist kein Array oder nicht definiert.");
setError(
"Konnte kueAlarm2 nicht finden oder es ist kein gültiges Array."
);
}
})
.catch((error) => {
console.error("Fehler beim Laden des Skripts:", error);
setError(error);
});
}, []);
const handleModuleClick = (rackNumber) => {
// Navigiere zu /kabelueberwachung und übermittle den rackNumber als Parameter
router.push(`/kabelueberwachung?rack=${rackNumber}`);
};
const renderBaugruppentraeger = () => {
const baugruppen = [];
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">
<div className="flex gap-1">
{slots.map((version, index) => {
const slotNumber = i * 8 + index + 1;
const moduleVersion = window.kueVersion
? window.kueVersion[slotNumber - 1]
: version;
const hasAlarm1 = kueAlarm1.includes(slotNumber);
const hasAlarm2 = kueAlarm2.includes(slotNumber);
const hasCableBreak = kueCableBreak.includes(slotNumber);
const alarmClass =
hasAlarm1 || hasAlarm2 || hasCableBreak
? "bg-red-500"
: "bg-white";
return (
<div
key={slotNumber}
className={`cursor-pointer ${alarmClass}`} // Sicherstellen, dass cursor-pointer hier verwendet wird
onClick={() => handleModuleClick(i + 1)} // Bei Klick navigieren
>
<KabelModulStatus
slot={slotNumber}
isOnline={version !== 0} // Prüfen, ob ein Modul online ist
moduleVersion={moduleVersion}
kueCableBreak={kueCableBreak}
/>
</div>
);
})}
</div>
</div>
);
}
return baugruppen;
};
return (
<div className="flex flex-col p-4">
{/* Letzte Meldungen - Titel und Icon Bereich */}
<div className="flex justify-between items-center w-full lg:w-2/3">
<div className="flex justify-between gap-1">
<Icon
icon="ri:calendar-schedule-line"
className="text-littwin-blue text-4xl"
/>
<h1 className="text-xl font-bold text-gray-700">
Letzten 20 Meldungen
</h1>
</div>
{/*
<Icon
icon="ph:trash"
className="text-red-500 hover:text-red-600 mr-8 text-3xl cursor-pointer"
/>
*/}
</div>
<div className="flex flex-col lg:flex-row gap-1 overflow-hidden">
{/* Meldungen Liste */}
<div className="bg-white shadow-md rounded-lg w-full lg:w-2/3 overflow-auto flex flex-grow">
<table className="min-w-full border border-gray-200 text-left">
<thead className="bg-gray-100 border-b border-gray-300">
<tr>
<th className="py-3 px-4 text-gray-700 text-sm font-medium">
ID
</th>
<th className="py-3 px-4 text-gray-700 text-sm font-medium">
Modul
</th>
<th className="py-3 px-4 text-gray-700 text-sm font-medium">
Zeitstempel
</th>
<th className="py-3 px-4 text-gray-700 text-sm font-medium w-2/3">
Meldung
</th>
<th className="py-3 px-4 text-gray-700 text-sm font-medium">
Status
</th>
</tr>
</thead>
<tbody className="text-xs text-gray-600 flex-shrink-0">
{last20Messages.length > 0 ? (
last20Messages.map((columns, index) => (
<tr
key={index}
className="border-b border-gray-200 hover:bg-gray-50"
>
<td className="py-1 px-4 w-1/7">{columns[0]}</td>
<td className="py-1 px-4 w-1/7">{columns[1]}</td>
<td className="py-1 px-4 w-3/7 whitespace-nowrap">
<div className="flex flex-row space-x-2">
<span>{columns[2].split(" ")[0]}</span>
<span>{columns[2].split(" ")[1]}</span>
</div>
</td>
<td className="py-1 px-4 w-2/7">{columns[3]}</td>
<td className="py-1 px-4 w-1/7">{columns[4]}</td>
</tr>
))
) : (
<tr>
<td className="py-3 px-4 text-center" colSpan="5">
Keine Meldungen verfügbar.
</td>
</tr>
)}
</tbody>
</table>
</div>
{/* Sidebar mit Informationen */}
<div className="shadow-md rounded-lg lg:w-1/3 flex flex-col gap-2">
{/* Versionsinformationen */}
<div className="bg-gray-50 p-4 rounded-lg shadow-sm border border-gray-200">
<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">
<span className="font-bold"></span> 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">
<span className="font-bold"> </span>Webserverversion: 1.0.0
</p>
</div>
</div>
{/* Beispiel für Geräteanzeige */}
<div className="bg-gray-50 rounded-lg shadow-sm border border-gray-200 justify-between space-y-1">
<div className="flex flex-row item-center justify-between p-1">
<CPLStatus />
{/*
<Access1Status />
<Access2Status />
*/}
</div>
<div className="flex flex-col item-center justify-between gap-1">
<div className="flex flex-row item-center justify-between space-y-0">
<div className="flex flex-col gap-1 mt-2">
{loading ? (
<p>Lädt...</p>
) : error ? (
<p className="text-red-500">{error.toString()}</p>
) : (
renderBaugruppentraeger()
)}
</div>
</div>
</div>
{/* Die Box für XIOPM1 und XIOPM2, jeweils unter CPL und Access 1 */}
<div className="grid grid-cols-3 gap-4 p-4">
<div className="flex justify-start items-center">
{/* <XioPM1Status />*/} {/* Unter CPL */}
</div>
<div className="flex justify-center items-center">
{/*<XioPM2Status /> */} {/* Unter Access 1 */}
</div>
</div>
</div>
</div>
</div>
{/* IP, Subnet und Gateway Informationen */}
<div className="flex-shrink-0 flex justify-between items-center mt-2 bg-white p-4 rounded-lg shadow-md border border-gray-200">
<div className="flex items-center space-x-4">
<img src="/images/IP-icon.svg" alt="IP Address" className="w-6 h-6" />
<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">
<img
src="/images/subnet-mask.svg"
alt="subnet mask"
className="w-6 h-6"
/>
<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">
<img src="/images/gateway.svg" alt="gateway" className="w-6 h-6" />
<div>
<p className="text-xs text-gray-500">Gateway</p>
<p className="text-sm font-medium text-gray-700">{gateway}</p>
</div>
</div>
</div>
</div>
);
}
export default Dashboard;

161
pages/einausgaenge.js Normal file
View File

@@ -0,0 +1,161 @@
"use client";
import React, { useState } from "react";
function Einausgaenge() {
// Beispiel-Daten für Ein- und Ausgänge (kannst du durch deine Daten ersetzen)
const inputs = [
{
id: 1,
name: "Eingang 1",
status: "Aktiv",
description: "Beschreibung 1",
},
{
id: 2,
name: "Eingang 2",
status: "Inaktiv",
description: "Beschreibung 2",
},
{
id: 3,
name: "Eingang 3",
status: "Aktiv",
description: "Beschreibung 3",
},
{
id: 4,
name: "Eingang 4",
status: "Inaktiv",
description: "Beschreibung 4",
},
];
const outputs = [
{
id: 1,
name: "Ausgang 1",
status: "Aktiv",
description: "Beschreibung 1",
},
{
id: 2,
name: "Ausgang 2",
status: "Inaktiv",
description: "Beschreibung 2",
},
{
id: 3,
name: "Ausgang 3",
status: "Aktiv",
description: "Beschreibung 3",
},
{
id: 4,
name: "Ausgang 4",
status: "Inaktiv",
description: "Beschreibung 4",
},
];
// Zustand für aktiven Tab (Ein- oder Ausgänge)
const [activeTab, setActiveTab] = useState("inputs");
return (
<div className="bg-gray-100 min-h-screen p-8">
{/* Tabs für Ein- und Ausgänge */}
<ul className="flex border-b border-gray-200">
<li
className={`mr-1 ${
activeTab === "inputs" ? "border-blue-500 text-blue-600" : ""
}`}
>
<button
onClick={() => setActiveTab("inputs")}
className={`inline-block px-4 py-2 rounded-t-lg ${
activeTab === "inputs"
? "bg-blue-500 text-white"
: "bg-white text-black hover:bg-gray-200"
}`}
>
Eingänge
</button>
</li>
<li
className={`mr-1 ${
activeTab === "outputs" ? "border-blue-500 text-blue-600" : ""
}`}
>
<button
onClick={() => setActiveTab("outputs")}
className={`inline-block px-4 py-2 rounded-t-lg ${
activeTab === "outputs"
? "bg-blue-500 text-white"
: "bg-white text-black hover:bg-gray-200"
}`}
>
Ausgänge
</button>
</li>
</ul>
{/* Inhalt für die aktiven Tabs */}
<div className="mt-4 p-4 bg-white rounded-lg shadow overflow-auto">
{activeTab === "inputs" && (
<div>
<h2 className="text-lg font-bold mb-4">Eingänge</h2>
<table className="min-w-full bg-white border border-gray-300">
<thead>
<tr className="w-full bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th className="py-3 px-4 text-left">ID</th>
<th className="py-3 px-4 text-left">Name</th>
<th className="py-3 px-4 text-left">Status</th>
<th className="py-3 px-4 text-left">Beschreibung</th>
</tr>
</thead>
<tbody className="text-gray-600 text-sm">
{inputs.map((input) => (
<tr key={input.id} className="border-b border-gray-200">
<td className="py-3 px-4 text-left">{input.id}</td>
<td className="py-3 px-4 text-left">{input.name}</td>
<td className="py-3 px-4 text-left">{input.status}</td>
<td className="py-3 px-4 text-left">{input.description}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
{activeTab === "outputs" && (
<div>
<h2 className="text-lg font-bold mb-4">Ausgänge</h2>
<table className="min-w-full bg-white border border-gray-300">
<thead>
<tr className="w-full bg-gray-200 text-gray-600 uppercase text-sm leading-normal">
<th className="py-3 px-4 text-left">ID</th>
<th className="py-3 px-4 text-left">Name</th>
<th className="py-3 px-4 text-left">Status</th>
<th className="py-3 px-4 text-left">Beschreibung</th>
</tr>
</thead>
<tbody className="text-gray-600 text-sm">
{outputs.map((output) => (
<tr key={output.id} className="border-b border-gray-200">
<td className="py-3 px-4 text-left">{output.id}</td>
<td className="py-3 px-4 text-left">{output.name}</td>
<td className="py-3 px-4 text-left">{output.status}</td>
<td className="py-3 px-4 text-left">
{output.description}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>
);
}
export default Einausgaenge;

BIN
pages/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

BIN
pages/fonts/GeistVF.woff Normal file

Binary file not shown.

17
pages/index.js Normal file
View File

@@ -0,0 +1,17 @@
import { useEffect } from "react";
import { useRouter } from "next/router";
export default function Home() {
const router = useRouter();
useEffect(() => {
// Prüft die Umgebung und hängt .html in der Produktion an
const isProduction = process.env.NEXT_PUBLIC_NODE_ENV === "production";
const dashboardPath = `/dashboard${isProduction ? ".html" : ""}`;
// Leitet den Benutzer sofort zur richtigen Dashboard-Seite weiter
router.replace(dashboardPath);
}, [router]);
return null; // Die Seite zeigt keinen Inhalt an und leitet sofort um
}

168
pages/kabelueberwachung.js Normal file
View File

@@ -0,0 +1,168 @@
"use client"; // app/kabelueberwachung/page.jsx
import React, { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import Kue705FO from "../components/modules/Kue705FO";
import { loadWindowVariables } from "../utils/loadWindowVariables";
function Kabelueberwachung() {
const router = useRouter();
const [activeRack, setActiveRack] = useState(1); // Track the active rack
const [kueIso, setKueIso] = useState([]); // State to store isolation values
const [kueID, setKueID] = useState([]); // State to store the KUE names
const [schleifenwiderstand, setSchleifenwiderstand] = useState([]); // State to store the resistance values
const [kueOnline, setKueOnline] = useState([]); // State to store the module status
// Verwende den useRouter Hook, um den Rack-Parameter zu extrahieren
useEffect(() => {
const query = new URLSearchParams(window.location.search);
const rackParam = query.get("rack");
if (rackParam) {
setActiveRack(parseInt(rackParam)); // Setze das aktive Rack basierend auf dem URL-Parameter
}
}, [router.query]);
// Load the external JavaScript file and fetch the isolation values
useEffect(() => {
loadWindowVariables()
.then(() => {
if (window.kueIso && Array.isArray(window.kueIso)) {
setKueIso(window.kueIso);
}
if (window.kueRes && Array.isArray(window.kueRes)) {
setSchleifenwiderstand(window.kueRes);
}
if (window.kueOnline && Array.isArray(window.kueOnline)) {
setKueOnline(window.kueOnline);
}
if (window.kueID && Array.isArray(window.kueID)) {
setKueID(window.kueID);
}
})
.catch((error) => {
console.error("Fehler beim Laden der Variablen:", error);
});
}, []);
// Zuerst alle Werte der Arrays speichern
const allModules = kueIso.map((iso, index) => ({
isolationswert: iso,
schleifenwiderstand: schleifenwiderstand[index],
modulName: kueID[index] || "Unknown",
kueOnlineStatus: kueOnline[index],
}));
// Dann die Module für jedes Rack in 8er-Gruppen aufteilen
const racks = {
rack1: allModules.slice(0, 8),
rack2: allModules.slice(8, 16),
rack3: allModules.slice(16, 24),
rack4: allModules.slice(24, 32),
};
// Log the racks in the console for debugging
/* console.log("Rack 1:", racks.rack1);
console.log("Rack 2:", racks.rack2);
console.log("Rack 3:", racks.rack3);
console.log("Rack 4:", racks.rack4); */
// Function to handle rack change
const changeRack = (rack) => {
setActiveRack(rack);
};
useEffect(() => {
const script = document.createElement("script");
// Dynamischer Pfad basierend auf der Umgebung
const environment = process.env.NEXT_PUBLIC_NODE_ENV || "production";
if (environment === "production") {
script.src = `CPL?/CPL/SERVICE/kueData.js`; // Produktions-Pfad
} else {
script.src = `/mockData/SERVICE/kueData.js`; // Mock-Daten-Pfad
}
script.async = true;
document.body.appendChild(script);
// Cleanup the script if the component unmounts
return () => {
document.body.removeChild(script);
};
}, []);
return (
<div className="bg-gray-100 flex-1 p-6 text-black">
<h1 className="text-2xl mb-4">Kabelüberwachung</h1>
<div className="mb-4">
<button
onClick={() => changeRack(1)}
className={`mr-1 ${
activeRack === 1
? "bg-littwin-blue text-white p-1 rounded-sm "
: "bg-gray-300 p-1 text-sm"
}`}
>
Rack 1
</button>
<button
onClick={() => changeRack(2)}
className={`mr-2 ${
activeRack === 2
? "bg-littwin-blue text-white p-1 rounded-sm"
: "bg-gray-300 p-1 text-sm"
}`}
>
Rack 2
</button>
<button
onClick={() => changeRack(3)}
className={`mr-2 ${
activeRack === 3
? "bg-littwin-blue text-white p-1 rounded-sm"
: "bg-gray-300 p-1 text-sm"
}`}
>
Rack 3
</button>
<button
onClick={() => changeRack(4)}
className={`mr-2 ${
activeRack === 4
? "bg-littwin-blue text-white p-1 rounded-sm"
: "bg-gray-300 p-1 text-sm"
}`}
>
Rack 4
</button>
</div>
<div className="flex flex-row space-x-4 scale-110 ml-[5%] mt-[5%]">
{racks[`rack${activeRack}`].map((slot, index) => {
const slotIndex = index + (activeRack - 1) * 8;
const alarmStatus =
(window.kueAlarm1 && window.kueAlarm1[slotIndex]) ||
(window.kueAlarm2 && window.kueAlarm2[slotIndex]) ||
(window.kueCableBreak && window.kueCableBreak[slotIndex]) ||
(window.kueGroundFault && window.kueGroundFault[slotIndex]);
return (
<div key={index} className="flex">
<Kue705FO
isolationswert={slot.isolationswert}
schleifenwiderstand={slot.schleifenwiderstand}
modulName={slot.modulName}
kueOnline={slot.kueOnlineStatus}
alarmStatus={alarmStatus} // Pass the calculated alarm status
slotIndex={slotIndex}
/>
{/*
console.log(`Module Data (Rack ${activeRack}, Slot ${index + 1}):`,slot);
*/}
</div>
);
})}
</div>
</div>
);
}
export default Kabelueberwachung;

135
pages/meldungen.js Normal file
View File

@@ -0,0 +1,135 @@
"use client";
import React, { useState } from "react";
function Meldungen() {
const [rowsPerPage, setRowsPerPage] = useState(25);
const [filter, setFilter] = useState("");
const [selectedMonth, setSelectedMonth] = useState("01");
const [selectedYear, setSelectedYear] = useState("23");
const [selectedDay, setSelectedDay] = useState("");
// Platzhalter für Meldungen
const messages = Array.from({ length: 50 }, (_, i) => ({
date: `2023-01-${String(i + 1).padStart(2, "0")}`,
message: `Meldung ${i + 1}`,
}));
const filteredMessages = messages.filter((msg) => {
const dayMatch = selectedDay
? msg.date.split("-")[2] === selectedDay.padStart(2, "0")
: true;
const monthMatch = msg.date.split("-")[1] === selectedMonth;
const yearMatch = msg.date.split("-")[0].substring(2) === selectedYear;
const filterMatch = msg.message
.toLowerCase()
.includes(filter.toLowerCase());
return dayMatch && monthMatch && yearMatch && filterMatch;
});
return (
<div className="flex flex-col h-full">
{/* Filterleiste */}
<div className="flex justify-between items-center bg-blue-500 p-4 text-white rounded-t">
<div className="flex items-center space-x-4">
<span>Interner Meldungsspeicher</span>
<select
value={selectedMonth}
onChange={(e) => setSelectedMonth(e.target.value)}
className="bg-white text-black p-1 rounded"
>
<option value="01">Januar</option>
<option value="02">Februar</option>
<option value="03">März</option>
<option value="04">April</option>
<option value="05">Mai</option>
<option value="06">Juni</option>
<option value="07">Juli</option>
<option value="08">August</option>
<option value="09">September</option>
<option value="10">Oktober</option>
<option value="11">November</option>
<option value="12">Dezember</option>
</select>
<select
value={selectedYear}
onChange={(e) => setSelectedYear(e.target.value)}
className="bg-white text-black p-1 rounded"
>
<option value="22">2022</option>
<option value="23">2023</option>
<option value="24">2024</option>
<option value="25">2025</option>
<option value="26">2026</option>
</select>
<input
type="number"
value={selectedDay}
onChange={(e) => setSelectedDay(e.target.value)}
min="1"
max="31"
placeholder="Tag"
className="bg-white text-black p-1 rounded"
/>
</div>
<div className="flex items-center space-x-4">
<input
type="text"
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Filter"
className="bg-white text-black p-1 rounded"
/>
<select
value={rowsPerPage}
onChange={(e) => setRowsPerPage(e.target.value)}
className="bg-white text-black p-1 rounded"
>
<option value="5">5</option>
<option value="10">10</option>
<option value="25">25</option>
<option value="50">50</option>
</select>
</div>
</div>
{/* Meldungen Tabelle */}
<div className="flex-grow overflow-auto mt-4">
<table className="min-w-full bg-white border border-gray-300">
<thead>
<tr className="bg-gray-200 text-gray-600 text-sm leading-normal">
<th className="py-3 px-4 text-left">Datum</th>
<th className="py-3 px-4 text-left">Meldung</th>
</tr>
</thead>
<tbody className="text-gray-600 text-sm">
{filteredMessages.slice(0, rowsPerPage).map((msg, index) => (
<tr key={index} className="border-b border-gray-200">
<td className="py-3 px-4">{msg.date}</td>
<td className="py-3 px-4">{msg.message}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Pagination */}
<div className="flex justify-center mt-4">
<ul className="flex space-x-2">
{Array.from(
{ length: Math.ceil(filteredMessages.length / rowsPerPage) },
(_, i) => (
<li
key={i}
className="cursor-pointer bg-blue-500 text-white p-2 rounded"
>
{i + 1}
</li>
)
)}
</ul>
</div>
</div>
);
}
export default Meldungen;

45
pages/wait.js Normal file
View File

@@ -0,0 +1,45 @@
"use client"; // app/wait/page.jsx
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { ClipLoader } from "react-spinners";
// IndexedDB functions only in the browser
let storePage, getPage;
if (typeof window !== "undefined") {
const indexedDBModule = require("../utils/indexedDB");
storePage = indexedDBModule.storePage;
getPage = indexedDBModule.getPage;
}
export default function WaitPage() {
const router = useRouter();
useEffect(() => {
// Store the page as a Blob in IndexedDB in the background
if (typeof window !== "undefined" && storePage) {
const pageContent = `
<div style="text-align: center; margin-top: 100px;">
<h1>Bitte warten...</h1>
<div class="spinner"></div>
<p>Die Seite wird automatisch neu geladen.</p>
</div>`;
storePage("waitPage", new Blob([pageContent], { type: "text/html" }));
}
// Timer to redirect after 20 seconds
const timer = setTimeout(() => {
router.push("/");
}, 20000);
// Cleanup timer when component is unmounted
return () => clearTimeout(timer);
}, [router]);
return (
<div style={{ textAlign: "center", marginTop: "100px" }}>
<h1>Bitte warten...</h1>
<ClipLoader color={"#76c7c0"} size={50} /> {/* Spinner */}
<p>Die Seite wird automatisch neu geladen.</p>
</div>
);
}