Compare commits
10 Commits
a596422056
...
001b237dd7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
001b237dd7 | ||
|
|
af21b180f1 | ||
|
|
fefff9419d | ||
|
|
27c60c6742 | ||
|
|
c8ec763aac | ||
|
|
d163df0d96 | ||
|
|
12d3a17f60 | ||
|
|
f3339ccafd | ||
|
|
fab8a02ce9 | ||
|
|
eb0585072d |
@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
|
|||||||
NEXT_PUBLIC_EXPORT_STATIC=false
|
NEXT_PUBLIC_EXPORT_STATIC=false
|
||||||
NEXT_PUBLIC_USE_CGI=false
|
NEXT_PUBLIC_USE_CGI=false
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.869
|
NEXT_PUBLIC_APP_VERSION=1.6.879
|
||||||
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)
|
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_EXPORT_STATIC=true
|
||||||
NEXT_PUBLIC_USE_CGI=true
|
NEXT_PUBLIC_USE_CGI=true
|
||||||
# App-Versionsnummer
|
# App-Versionsnummer
|
||||||
NEXT_PUBLIC_APP_VERSION=1.6.869
|
NEXT_PUBLIC_APP_VERSION=1.6.879
|
||||||
NEXT_PUBLIC_CPL_MODE=production
|
NEXT_PUBLIC_CPL_MODE=production
|
||||||
50
CHANGELOG.md
50
CHANGELOG.md
@@ -1,3 +1,53 @@
|
|||||||
|
## [1.6.879] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode Modale
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.878] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode Berichte
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.877] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode Modale
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.876] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode Baugrüppenträger sttus
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.875] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.874] – 2025-09-08
|
||||||
|
|
||||||
|
- fix: TDR 2 Minuten eingestellt laut eingabe
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.873] – 2025-09-08
|
||||||
|
|
||||||
|
- fix: TDR 2 Minuten eingestellt laut eingaben
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.872] – 2025-09-08
|
||||||
|
|
||||||
|
- fix: TDR 2 Minuten eingestellt laut eingaben
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.871] – 2025-09-08
|
||||||
|
|
||||||
|
- WIP: dark mode
|
||||||
|
|
||||||
|
---
|
||||||
|
## [1.6.870] – 2025-09-08
|
||||||
|
|
||||||
|
- fix: Beim Aufruf der TDR-Detailseite erscheint im Hintergrund auf der KÜ ein Schleifenwiderstand von 0 KOhm. In der Daten Javascriptdatei steht jedoch der richtige Wert.
|
||||||
|
|
||||||
|
---
|
||||||
## [1.6.869] – 2025-09-08
|
## [1.6.869] – 2025-09-08
|
||||||
|
|
||||||
- fix: Beim Ausführen einer TDR-Messung (Klick auf blauen Button in der TDR-Detailseite) erscheint keine Rückmeldung. Dort müsste ein Hinweis erscheinen “TDR-Messung wird ausgeführt und kann bis zu zwei Minuten dauern”
|
- fix: Beim Ausführen einer TDR-Messung (Klick auf blauen Button in der TDR-Detailseite) erscheint keine Rückmeldung. Dort müsste ein Hinweis erscheinen “TDR-Messung wird ausgeführt und kann bis zu zwei Minuten dauern”
|
||||||
|
|||||||
@@ -55,45 +55,57 @@ function Footer() {
|
|||||||
}, [showSlider]);
|
}, [showSlider]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="relative bg-gray-300 p-4 xl:p-2 2xl:p-4 overflow-hidden text-black laptop:h-[5vh] ">
|
<footer className="relative bg-[var(--color-surface-alt)] border-t border-base p-4 xl:p-2 2xl:p-4 overflow-hidden text-[var(--color-fg)] laptop:h-[5vh] theme-transition">
|
||||||
<div className="container mx-auto flex justify-between">
|
<div className="container mx-auto flex justify-between">
|
||||||
<div className="flex flex-row space-x-2">
|
<div className="flex flex-row space-x-2 items-center">
|
||||||
<Icon
|
<Icon
|
||||||
icon="material-symbols:factory-outline"
|
icon="material-symbols:factory-outline"
|
||||||
className="text-xl text-blue-400"
|
className="text-xl text-accent"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm">Littwin Systemtechnik GmbH & Co. KG</p>
|
<p className="text-sm text-fg-muted">
|
||||||
|
Littwin Systemtechnik GmbH & Co. KG
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row space-x-2">
|
<div className="flex flex-row space-x-2 items-center">
|
||||||
<Icon icon="charm:phone" className="text-xl text-blue-400" />
|
<Icon icon="charm:phone" className="text-xl text-accent" />
|
||||||
<p className="text-sm">Telefon: 04402 972577-0</p>
|
<p className="text-sm text-fg-muted">Telefon: 04402 972577-0</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-row space-x-2">
|
<div className="flex flex-row space-x-2 items-center">
|
||||||
<Icon icon="mdi:email-outline" className="text-xl text-blue-400" />
|
<Icon icon="mdi:email-outline" className="text-xl text-accent" />
|
||||||
<p className="text-sm">kontakt@littwin-systemtechnik.de</p>
|
<p className="text-sm text-fg-muted">
|
||||||
|
kontakt@littwin-systemtechnik.de
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="flex flex-row space-x-2 cursor-pointer"
|
className="flex flex-row space-x-2 cursor-pointer items-center group"
|
||||||
onClick={() => setShowSlider(true)}
|
onClick={() => setShowSlider(true)}
|
||||||
>
|
>
|
||||||
<Icon icon="bi:book" className="text-xl text-blue-400" />
|
<Icon
|
||||||
<p className="text-sm">Handbücher</p>
|
icon="bi:book"
|
||||||
|
className="text-xl text-accent group-hover:brightness-110 transition"
|
||||||
|
/>
|
||||||
|
<p className="text-sm text-fg-muted group-hover:text-[var(--color-fg)] transition">
|
||||||
|
Handbücher
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
ref={sliderRef}
|
ref={sliderRef}
|
||||||
className={`fixed top-0 right-0 w-64 h-full bg-white shadow-lg transform transition-transform duration-300 ${
|
className={`fixed top-0 right-0 w-64 h-full bg-[var(--color-surface)] border-l border-base shadow-lg transform transition-transform duration-300 ${
|
||||||
showSlider ? "translate-x-0" : "translate-x-full"
|
showSlider ? "translate-x-0" : "translate-x-full"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="p-4 flex justify-between items-center border-b">
|
<div className="p-4 flex justify-between items-center border-b border-base">
|
||||||
<h3 className="text-lg font-semibold">PDF Handbücher</h3>
|
<h3 className="text-base font-semibold text-[var(--color-fg)]">
|
||||||
|
PDF Handbücher
|
||||||
|
</h3>
|
||||||
<button
|
<button
|
||||||
className="text-gray-500 hover:text-gray-800"
|
className="text-[var(--color-muted)] hover:text-[var(--color-fg)] transition"
|
||||||
onClick={() => setShowSlider(false)}
|
onClick={() => setShowSlider(false)}
|
||||||
|
aria-label="Schließen"
|
||||||
>
|
>
|
||||||
<Icon icon="carbon:close" className="text-2xl" />
|
<Icon icon="carbon:close" className="text-xl" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -102,7 +114,7 @@ function Footer() {
|
|||||||
{pdfFiles.map((fileName) => (
|
{pdfFiles.map((fileName) => (
|
||||||
<li
|
<li
|
||||||
key={fileName}
|
key={fileName}
|
||||||
className="cursor-pointer text-blue-500 hover:underline mb-2"
|
className="cursor-pointer text-accent hover:underline mb-2 text-sm"
|
||||||
onClick={() => loadPDF(fileName)}
|
onClick={() => loadPDF(fileName)}
|
||||||
>
|
>
|
||||||
{fileName}
|
{fileName}
|
||||||
|
|||||||
@@ -109,21 +109,43 @@ function Header() {
|
|||||||
}, [deviceName, dispatch]);
|
}, [deviceName, dispatch]);
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
// Dark/Light Mode Toggle
|
// Dark/Light Mode Toggle (persisted)
|
||||||
const [isDark, setIsDark] = useState(false);
|
const [isDark, setIsDark] = useState(false);
|
||||||
|
// Initial state from html class / localStorage (set by _document script before hydration)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window === "undefined") return;
|
||||||
|
const html = document.documentElement;
|
||||||
|
const stored = localStorage.getItem("theme");
|
||||||
|
const active = stored ? stored === "dark" : html.classList.contains("dark");
|
||||||
|
setIsDark(active);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
if (isDark) {
|
if (isDark) {
|
||||||
html.classList.add("dark");
|
html.classList.add("dark");
|
||||||
|
localStorage.setItem("theme", "dark");
|
||||||
} else {
|
} else {
|
||||||
html.classList.remove("dark");
|
html.classList.remove("dark");
|
||||||
}
|
localStorage.setItem("theme", "light");
|
||||||
}
|
}
|
||||||
}, [isDark]);
|
}, [isDark]);
|
||||||
|
|
||||||
|
// Keyboard shortcut Alt + D to toggle theme
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = (e: KeyboardEvent) => {
|
||||||
|
if (e.altKey && (e.key === "d" || e.key === "D")) {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDark((d) => !d);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("keydown", handler);
|
||||||
|
return () => window.removeEventListener("keydown", handler);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<header className="bg-gray-300 dark:bg-gray-800 flex justify-between items-center w-full h-[13vh] laptop:h-[10vh] relative text-black dark:text-white ">
|
<header className="bg-[var(--color-surface)] dark:bg-[var(--color-surface)]/90 backdrop-blur flex justify-between items-center w-full h-[13vh] laptop:h-[10vh] relative text-[var(--color-fg)] dark:text-[var(--color-fg)] shadow-sm border-b border-[var(--color-border)]">
|
||||||
<div
|
<div
|
||||||
className="absolute transform -translate-y-1/2
|
className="absolute transform -translate-y-1/2
|
||||||
left-[8%] sm:left-[8%] md:left-[8%] lg:left-[8%] xl:left-[6%] 2xl:left-[2%] laptop:left-[4%] laptop:
|
left-[8%] sm:left-[8%] md:left-[8%] lg:left-[8%] xl:left-[6%] 2xl:left-[2%] laptop:left-[4%] laptop:
|
||||||
@@ -154,10 +176,10 @@ function Header() {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col leading-tight whitespace-nowrap">
|
<div className="flex flex-col leading-tight whitespace-nowrap">
|
||||||
<h2 className="text-xl laptop:text-base xl:text-lg font-bold text-gray-900 dark:text-gray-100">
|
<h2 className="text-xl laptop:text-base xl:text-lg font-bold text-[var(--color-fg)]">
|
||||||
Meldestation
|
Meldestation
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-gray-600 dark:text-gray-300 text-lg laptop:text-sm xl:text-base truncate max-w-[20vw]">
|
<p className="text-[var(--color-fg-muted)] text-lg laptop:text-sm xl:text-base truncate max-w-[20vw]">
|
||||||
{deviceName}
|
{deviceName}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -169,7 +191,7 @@ function Header() {
|
|||||||
<button
|
<button
|
||||||
aria-label={isDark ? "Light Mode" : "Dark Mode"}
|
aria-label={isDark ? "Light Mode" : "Dark Mode"}
|
||||||
onClick={() => setIsDark((d) => !d)}
|
onClick={() => setIsDark((d) => !d)}
|
||||||
className="rounded-full p-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 transition"
|
className="rounded-full p-2 bg-[var(--color-surface-alt)]/80 hover:bg-[var(--color-surface-alt)] dark:bg-[var(--color-surface-alt)]/60 dark:hover:bg-[var(--color-surface-alt)] transition border border-[var(--color-border)]"
|
||||||
title={isDark ? "Light Mode" : "Dark Mode"}
|
title={isDark ? "Light Mode" : "Dark Mode"}
|
||||||
>
|
>
|
||||||
{isDark ? (
|
{isDark ? (
|
||||||
@@ -192,7 +214,7 @@ function Header() {
|
|||||||
<button
|
<button
|
||||||
onClick={handleLogout}
|
onClick={handleLogout}
|
||||||
aria-label="Abmelden"
|
aria-label="Abmelden"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded"
|
className="px-4 py-2 rounded bg-[var(--color-accent)] text-white hover:brightness-110 shadow-sm focus:outline-none focus:ring-2 focus:ring-[var(--color-ring)] focus:ring-offset-2 focus:ring-offset-[var(--color-background)] transition"
|
||||||
>
|
>
|
||||||
Abmelden
|
Abmelden
|
||||||
</button>
|
</button>
|
||||||
@@ -202,7 +224,7 @@ function Header() {
|
|||||||
|
|
||||||
{/* Warnhinweis, wenn der Admin angemeldet ist */}
|
{/* Warnhinweis, wenn der Admin angemeldet ist */}
|
||||||
{isAdminLoggedIn && (
|
{isAdminLoggedIn && (
|
||||||
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-full xl:w-1/4 2xl:w-1/4 h-8 bg-yellow-400 text-center py-2 text-black font-bold">
|
<div className="absolute top-0 left-1/2 transform -translate-x-1/2 w-full xl:w-1/4 2xl:w-1/4 h-8 bg-[var(--color-warning)] text-center py-2 text-black font-bold tracking-wide">
|
||||||
Admin-Modus aktiv
|
Admin-Modus aktiv
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 shadow-md border border-gray-200 dark:border-gray-700 p-3 rounded-lg laptop:p-1 xl:p-1 ${
|
className={`text-[var(--color-fg)] bg-[var(--color-surface)] dark:bg-[var(--color-surface)] shadow-sm border border-[var(--color-border)] p-3 rounded-lg laptop:p-1 xl:p-1 ${
|
||||||
loading ? "cursor-wait" : ""
|
loading ? "cursor-wait opacity-70" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
||||||
@@ -54,24 +54,24 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
loading ? "cursor-wait" : ""
|
loading ? "cursor-wait" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<thead className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-b items-center">
|
<thead className="bg-[var(--color-surface-alt)]/60 dark:bg-[var(--color-surface-alt)]/30 text-[var(--color-fg)] border-b border-[var(--color-border)] items-center">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Eingang
|
Eingang
|
||||||
</th>
|
</th>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Messwert
|
Messwert
|
||||||
</th>
|
</th>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Einheit
|
Einheit
|
||||||
</th>
|
</th>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Bezeichnung
|
Bezeichnung
|
||||||
</th>
|
</th>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Einstellungen
|
Einstellungen
|
||||||
</th>
|
</th>
|
||||||
<th className="border p-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="border p-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
Messkurve
|
Messkurve
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -92,12 +92,12 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
loading
|
loading
|
||||||
? "cursor-wait"
|
? "cursor-wait"
|
||||||
: analogInput.id === activeId
|
: analogInput.id === activeId
|
||||||
? "bg-blue-100 dark:bg-gray-700 dark:text-white"
|
? "bg-[var(--color-accent-soft)] dark:bg-[var(--color-surface-alt)]/60 text-[var(--color-fg)]"
|
||||||
: "hover:bg-gray-100 dark:hover:bg-gray-800"
|
: "hover:bg-[var(--color-surface-alt)]/70 dark:hover:bg-[var(--color-surface-alt)]/30"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<td
|
<td
|
||||||
className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"
|
className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]"
|
||||||
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center gap-1 ">
|
<div className="flex items-center gap-1 ">
|
||||||
@@ -109,7 +109,7 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className="border p-2 text-right bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"
|
className="border p-2 text-right bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]"
|
||||||
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
||||||
>
|
>
|
||||||
{typeof analogInput.value === "number"
|
{typeof analogInput.value === "number"
|
||||||
@@ -118,19 +118,19 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td
|
<td
|
||||||
className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"
|
className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]"
|
||||||
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
||||||
>
|
>
|
||||||
{analogInput.unit || "-"}
|
{analogInput.unit || "-"}
|
||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100"
|
className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]"
|
||||||
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
onClick={() => handleSelect(analogInput.id!, analogInput)}
|
||||||
>
|
>
|
||||||
{analogInput.label || "----"}
|
{analogInput.label || "----"}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td className="border p-2 text-center bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 text-center bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSelect(analogInput.id!, analogInput);
|
handleSelect(analogInput.id!, analogInput);
|
||||||
@@ -141,7 +141,7 @@ export default function AnalogInputsTable({ loading }: { loading: boolean }) {
|
|||||||
<Icon icon={settingsIcon} className="text-xl" />
|
<Icon icon={settingsIcon} className="text-xl" />
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 text-center bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 text-center bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleSelect(analogInput.id!, analogInput);
|
handleSelect(analogInput.id!, analogInput);
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ function AnalogInputsView() {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="grid grid-cols-1 gap-4 justify-items-start">
|
<div className="grid grid-cols-1 gap-4 justify-items-start">
|
||||||
<div className="bg-white dark:bg-gray-900 rounded-lg p-4 max-w-3xl text-gray-900 dark:text-gray-100">
|
<div className="rounded-lg p-4 max-w-3xl text-[var(--color-fg)] bg-[var(--color-surface)] dark:bg-[var(--color-surface)] border border-[var(--color-border)] shadow-sm">
|
||||||
<h2 className="text-xl font-semibold mb-4 text-gray-900 dark:text-gray-100">
|
<h2 className="text-xl font-semibold mb-4 text-[var(--color-fg)] tracking-wide">
|
||||||
Messwerteingänge
|
Messwerteingänge
|
||||||
</h2>
|
</h2>
|
||||||
<AnalogInputsTable loading={loading} />
|
<AnalogInputsTable loading={loading} />
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ const Baugruppentraeger: React.FC = () => {
|
|||||||
baugruppen.push(
|
baugruppen.push(
|
||||||
<div
|
<div
|
||||||
key={i}
|
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"
|
className="flex card mb-4 xl:mb-0 lg:mb-0 w-full laptop:scale-y-75 xl:scale-y-90"
|
||||||
>
|
>
|
||||||
<div className="flex gap-1">
|
<div className="flex gap-1">
|
||||||
{slots.map((version, index) => {
|
{slots.map((version, index) => {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const DashboardView: React.FC = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
return (
|
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 bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
<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 bg-[var(--color-background)] text-[var(--color-fg)]">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex justify-between items-center w-full lg:w-2/3">
|
<div className="flex justify-between items-center w-full lg:w-2/3">
|
||||||
<div className="flex justify-between gap-1">
|
<div className="flex justify-between gap-1">
|
||||||
@@ -33,7 +33,7 @@ const DashboardView: React.FC = () => {
|
|||||||
icon="ri:calendar-schedule-line"
|
icon="ri:calendar-schedule-line"
|
||||||
className="text-littwin-blue text-4xl xl:text-2xl"
|
className="text-littwin-blue text-4xl xl:text-2xl"
|
||||||
/>
|
/>
|
||||||
<h1 className="text-xl font-bold text-gray-900 dark:text-gray-100 xl:text-base">
|
<h1 className="text-xl font-bold xl:text-base text-[var(--color-fg)] tracking-wide">
|
||||||
Letzten 20 Meldungen
|
Letzten 20 Meldungen
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,22 +48,22 @@ export default function Last20MessagesTable({ className }: Props) {
|
|||||||
return (
|
return (
|
||||||
<div className={`flex flex-col gap-3 p-4 ${className}`}>
|
<div className={`flex flex-col gap-3 p-4 ${className}`}>
|
||||||
<div className="overflow-auto max-h-[80vh]">
|
<div className="overflow-auto max-h-[80vh]">
|
||||||
<table className="min-w-full border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<table className="min-w-full border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<thead className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 text-left sticky top-0 z-10">
|
<thead className="text-left sticky top-0 z-10 bg-[var(--color-surface-alt)]/70 dark:bg-[var(--color-surface-alt)]/25 text-[var(--color-fg)]">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Prio
|
Prio
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Zeitstempel
|
Zeitstempel
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Quelle
|
Quelle
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Meldung
|
Meldung
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -72,24 +72,24 @@ export default function Last20MessagesTable({ className }: Props) {
|
|||||||
{filteredMessages.slice(0, 20).map((msg, index) => (
|
{filteredMessages.slice(0, 20).map((msg, index) => (
|
||||||
<tr
|
<tr
|
||||||
key={index}
|
key={index}
|
||||||
className="hover:bg-gray-100 dark:hover:bg-gray-800"
|
className="hover:bg-[var(--color-surface-alt)]/70 dark:hover:bg-[var(--color-surface-alt)]/30 transition"
|
||||||
>
|
>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
<div
|
<div
|
||||||
className="w-4 h-4 rounded"
|
className="w-4 h-4 rounded"
|
||||||
style={{ backgroundColor: msg.c }}
|
style={{ backgroundColor: msg.c }}
|
||||||
></div>
|
></div>
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
{msg.t}
|
{msg.t}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
{msg.i}
|
{msg.i}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
{msg.m}
|
{msg.m}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border p-2 bg-[var(--color-surface)] text-[var(--color-fg)] border-[var(--color-border)]">
|
||||||
{msg.v}
|
{msg.v}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -97,7 +97,7 @@ export default function Last20MessagesTable({ className }: Props) {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{messages.length === 0 && (
|
{messages.length === 0 && (
|
||||||
<div className="mt-4 text-center text-gray-500 italic dark:text-gray-400">
|
<div className="mt-4 text-center italic text-[var(--color-fg-muted)]">
|
||||||
Keine Meldungen im gewählten Zeitraum vorhanden.
|
Keine Meldungen im gewählten Zeitraum vorhanden.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ const NetworkInfo: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full flex-direction: row flex">
|
<div className="w-full flex-direction: row flex">
|
||||||
<div className=" flex-grow flex justify-between items-center mt-1 bg-white dark:bg-gray-800 p-2 rounded-lg shadow-md border border-gray-200 dark:border-gray-700 laptop:m-0 laptop:scale-y-75 2xl:scale-y-75">
|
<div className=" flex-grow flex justify-between items-center mt-1 p-2 rounded-lg shadow-sm bg-[var(--color-surface)] dark:bg-[var(--color-surface)] border border-[var(--color-border)] laptop:m-0 laptop:scale-y-75 2xl:scale-y-75">
|
||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<Image
|
<Image
|
||||||
src="/images/IP-icon.svg"
|
src="/images/IP-icon.svg"
|
||||||
@@ -49,12 +49,8 @@ const NetworkInfo: React.FC = () => {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
<p className="text-xs text-[var(--color-fg-muted)]">IP-Adresse</p>
|
||||||
IP-Adresse
|
<p className="text-sm font-medium text-[var(--color-fg)]">{ip}</p>
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
||||||
{ip}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -68,10 +64,8 @@ const NetworkInfo: React.FC = () => {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">
|
<p className="text-xs text-[var(--color-fg-muted)]">Subnet-Maske</p>
|
||||||
Subnet-Maske
|
<p className="text-sm font-medium text-[var(--color-fg)]">
|
||||||
</p>
|
|
||||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
|
||||||
{subnet}
|
{subnet}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -87,8 +81,8 @@ const NetworkInfo: React.FC = () => {
|
|||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">Gateway</p>
|
<p className="text-xs text-[var(--color-fg-muted)]">Gateway</p>
|
||||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
<p className="text-sm font-medium text-[var(--color-fg)]">
|
||||||
{gateway}
|
{gateway}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -97,8 +91,8 @@ const NetworkInfo: React.FC = () => {
|
|||||||
<div className="flex items-center space-x-4">
|
<div className="flex items-center space-x-4">
|
||||||
<div className="text-xs font-bold text-littwin-blue">OPC-UA</div>
|
<div className="text-xs font-bold text-littwin-blue">OPC-UA</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="text-xs text-gray-500 dark:text-gray-400">Status</p>
|
<p className="text-xs text-[var(--color-fg-muted)]">Status</p>
|
||||||
<p className="text-sm font-medium text-gray-700 dark:text-gray-200">
|
<p className="text-sm font-medium text-[var(--color-fg)]">
|
||||||
{opcUaZustand}
|
{opcUaZustand}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,9 +4,7 @@ import { Icon } from "@iconify/react";
|
|||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { RootState } from "../../../redux/store";
|
import { RootState } from "../../../redux/store";
|
||||||
|
|
||||||
type VersionInfoProps = {
|
type VersionInfoProps = { className?: string };
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
||||||
const appVersion =
|
const appVersion =
|
||||||
@@ -17,26 +15,26 @@ const VersionInfo: React.FC<VersionInfoProps> = ({ className = "" }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`card w-full p-3 laptop:p-2 ${className}`}>
|
||||||
className={`bg-gray-50 dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 w-full laptop:p-2 ${className}`}
|
<h2 className="text-base font-semibold mb-2 text-[var(--color-fg)]">
|
||||||
>
|
|
||||||
<h2 className="text-lg font-semibold text-gray-700 dark:text-gray-200 mb-2">
|
|
||||||
Versionsinformationen
|
Versionsinformationen
|
||||||
</h2>
|
</h2>
|
||||||
|
<ul className="space-y-1">
|
||||||
<div className="flex flex-row p-2 space-x-2">
|
<li className="flex items-start gap-2">
|
||||||
<Icon icon="bx:code-block" className="text-xl text-blue-400" />
|
<Icon icon="bx:code-block" className="text-xl text-accent" />
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
<p className="text-sm text-fg-muted">
|
||||||
Applikationsversion: {appVersion}
|
Applikationsversion:{" "}
|
||||||
|
<span className="text-[var(--color-fg)]">{appVersion}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</li>
|
||||||
|
<li className="flex items-start gap-2">
|
||||||
<div className="flex flex-row p-2 space-x-2">
|
<Icon icon="mdi:web" className="text-xl text-accent" />
|
||||||
<Icon icon="mdi:web" className="text-xl text-blue-400" />
|
<p className="text-sm text-fg-muted">
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-300">
|
Webversion:{" "}
|
||||||
Webversion: {webVersion}
|
<span className="text-[var(--color-fg)]">{webVersion}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ export default function DigitalInputsWidget({
|
|||||||
//console.log("DigitalInputs", inputs);
|
//console.log("DigitalInputs", inputs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 shadow-md border border-gray-200 dark:border-gray-700 p-3 rounded-lg w-full laptop:p-1 xl:p-1">
|
<div className="text-[var(--color-fg)] bg-[var(--color-surface)] dark:bg-[var(--color-surface)] shadow-sm border border-[var(--color-border)] p-3 rounded-lg w-full laptop:p-1 xl:p-1">
|
||||||
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
||||||
<Icon
|
<Icon
|
||||||
icon={inputIcon}
|
icon={inputIcon}
|
||||||
@@ -38,19 +38,19 @@ export default function DigitalInputsWidget({
|
|||||||
/>
|
/>
|
||||||
Meldungseingänge {inputRange.start + 1} – {inputRange.end}
|
Meldungseingänge {inputRange.start + 1} – {inputRange.end}
|
||||||
</h2>
|
</h2>
|
||||||
<table className="w-full text-xs laptop:text-[10px] xl:text-xs 2xl:text-sm border-collapse bg-white dark:bg-gray-900">
|
<table className="w-full text-xs laptop:text-[10px] xl:text-xs 2xl:text-sm border-collapse bg-[var(--color-surface)]">
|
||||||
<thead className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-b">
|
<thead className="bg-[var(--color-surface-alt)]/60 dark:bg-[var(--color-surface-alt)]/25 text-[var(--color-fg)] border-b border-[var(--color-border)]">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Eingang
|
Eingang
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Zustand
|
Zustand
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Bezeichnung
|
Bezeichnung
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Aktion
|
Aktion
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -59,9 +59,9 @@ export default function DigitalInputsWidget({
|
|||||||
{inputs.map((input) => (
|
{inputs.map((input) => (
|
||||||
<tr
|
<tr
|
||||||
key={input.id}
|
key={input.id}
|
||||||
className="border-b hover:bg-gray-100 dark:hover:bg-gray-800"
|
className="border-b border-[var(--color-border)] hover:bg-[var(--color-surface-alt)]/70 dark:hover:bg-[var(--color-surface-alt)]/30 transition"
|
||||||
>
|
>
|
||||||
<td className="px-1 py-0 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-0 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<div className="flex items-center gap-1 ">
|
<div className="flex items-center gap-1 ">
|
||||||
<Icon
|
<Icon
|
||||||
icon={loginIcon}
|
icon={loginIcon}
|
||||||
@@ -70,7 +70,7 @@ export default function DigitalInputsWidget({
|
|||||||
{input.id}
|
{input.id}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
{input.eingangOffline ? (
|
{input.eingangOffline ? (
|
||||||
<div className="relative group inline-block">
|
<div className="relative group inline-block">
|
||||||
<span className="text-red-500 sm:text-sm md:text-base lg:text-lg xl:text-xl 2xl:text-2xl laptop:text-sm ">
|
<span className="text-red-500 sm:text-sm md:text-base lg:text-lg xl:text-xl 2xl:text-2xl laptop:text-sm ">
|
||||||
@@ -91,10 +91,10 @@ export default function DigitalInputsWidget({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
{input.label}
|
{input.label}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<Icon
|
<Icon
|
||||||
icon={settingsIcon}
|
icon={settingsIcon}
|
||||||
className="text-gray-400 text-base laptop:text-sm xl:text-sm 2xl:text-lg cursor-pointer dark:text-gray-300 dark:hover:text-white"
|
className="text-gray-400 text-base laptop:text-sm xl:text-sm 2xl:text-lg cursor-pointer dark:text-gray-300 dark:hover:text-white"
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default function DigitalOutputsWidget({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 shadow-md border border-gray-200 dark:border-gray-700 p-3 rounded-lg w-full h-fit max-h-[400px] overflow-auto">
|
<div className="bg-[var(--color-surface)] text-[var(--color-fg)] shadow-md border border-base p-3 rounded-lg w-full h-fit max-h-[400px] overflow-auto">
|
||||||
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
<h2 className="laptop:text-sm md:text-base 2xl:text-lg font-bold mb-3 flex items-center">
|
||||||
<Icon
|
<Icon
|
||||||
icon={outputIcon}
|
icon={outputIcon}
|
||||||
@@ -74,19 +74,19 @@ export default function DigitalOutputsWidget({
|
|||||||
/>
|
/>
|
||||||
Schaltausgänge
|
Schaltausgänge
|
||||||
</h2>
|
</h2>
|
||||||
<table className="w-full text-xs laptop:text-[10px] xl:text-xs 2xl:text-sm border-collapse bg-white dark:bg-gray-900 rounded-lg">
|
<table className="w-full text-xs laptop:text-[10px] xl:text-xs 2xl:text-sm border-collapse bg-[var(--color-surface)] rounded-lg">
|
||||||
<thead className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 border-b">
|
<thead className="bg-[var(--color-surface)] text-[var(--color-fg)] border-b border-base">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Ausgang
|
Ausgang
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Bezeichnung
|
Bezeichnung
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Schalter
|
Schalter
|
||||||
</th>
|
</th>
|
||||||
<th className="px-1 py-1 text-left bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="px-1 py-1 text-left bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
Aktion
|
Aktion
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -95,33 +95,33 @@ export default function DigitalOutputsWidget({
|
|||||||
{digitalOutputs.map((output) => (
|
{digitalOutputs.map((output) => (
|
||||||
<tr
|
<tr
|
||||||
key={output.id}
|
key={output.id}
|
||||||
className="border-b hover:bg-gray-100 dark:hover:bg-gray-800"
|
className="border-b border-base hover:bg-[var(--color-surface-alt)] transition-colors"
|
||||||
>
|
>
|
||||||
<td className="flex items-center px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="flex items-center px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<Icon
|
<Icon
|
||||||
icon={outputIcon}
|
icon={outputIcon}
|
||||||
className="text-gray-600 mr-1 text-base"
|
className="text-[var(--color-muted)] mr-1 text-base"
|
||||||
/>
|
/>
|
||||||
{output.id}
|
{output.id}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
{output.label}
|
{output.label}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<Icon
|
<Icon
|
||||||
icon={switchIcon}
|
icon={switchIcon}
|
||||||
className={`cursor-pointer text-base transition ${
|
className={`cursor-pointer text-base transition ${
|
||||||
output.status
|
output.status
|
||||||
? "text-littwin-blue"
|
? "text-accent"
|
||||||
: "text-gray-500 scale-x-[-1]"
|
: "text-[var(--color-muted)] scale-x-[-1]"
|
||||||
} dark:hover:text-littwin-blue`}
|
} hover:text-accent`}
|
||||||
onClick={() => handleToggle(output.id)}
|
onClick={() => handleToggle(output.id)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-1 py-1 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="px-1 py-1 bg-[var(--color-surface)] text-[var(--color-fg)]">
|
||||||
<Icon
|
<Icon
|
||||||
icon={settingsIcon}
|
icon={settingsIcon}
|
||||||
className="text-gray-400 text-base cursor-pointer dark:text-gray-300 dark:hover:text-white"
|
className="text-[var(--color-muted)] text-base cursor-pointer hover:text-[var(--color-fg)]"
|
||||||
onClick={() => openOutputModal(output)}
|
onClick={() => openOutputModal(output)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -119,19 +119,23 @@ function KabelueberwachungView() {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
{[1, 2, 3, 4].map((rack) => (
|
{[1, 2, 3, 4].map((rack) => {
|
||||||
|
const isActive = Number(activeRack) === Number(rack);
|
||||||
|
return (
|
||||||
<button
|
<button
|
||||||
key={rack}
|
key={rack}
|
||||||
onClick={() => changeRack(rack)}
|
onClick={() => changeRack(rack)}
|
||||||
className={`mr-2 ${
|
aria-pressed={isActive}
|
||||||
Number(activeRack) === Number(rack)
|
className={`mr-2 px-2 py-1 rounded-sm text-sm font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-accent/50 ${
|
||||||
? "bg-littwin-blue text-white p-1 rounded-sm"
|
isActive
|
||||||
: "bg-gray-300 p-1 text-sm"
|
? "btn-primary"
|
||||||
|
: "btn-muted text-fg opacity-90 hover:opacity-100"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
Rack {rack}
|
Rack {rack}
|
||||||
</button>
|
</button>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</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%]">
|
<div className="flex flex-row space-x-8 xl:space-x-0 2xl:space-x-8 qhd:space-x-16 ml-[5%] mt-[5%]">
|
||||||
{(
|
{(
|
||||||
|
|||||||
@@ -276,7 +276,9 @@ const TDRChartActionBar: React.FC = () => {
|
|||||||
{tdrRunning && (
|
{tdrRunning && (
|
||||||
<div className="fixed inset-0 z-[1000] flex flex-col items-center justify-center bg-white/80 backdrop-blur-sm">
|
<div className="fixed inset-0 z-[1000] flex flex-col items-center justify-center bg-white/80 backdrop-blur-sm">
|
||||||
<div className="mb-4 text-center space-y-1">
|
<div className="mb-4 text-center space-y-1">
|
||||||
<p className="text-lg font-semibold">TDR Messung läuft</p>
|
<p className="text-lg font-semibold">
|
||||||
|
TDR Messung läuft... kann bis zu zwei Minuten dauern
|
||||||
|
</p>
|
||||||
<p className="text-sm text-gray-700">
|
<p className="text-sm text-gray-700">
|
||||||
Bitte warten…{" "}
|
Bitte warten…{" "}
|
||||||
{Math.min(
|
{Math.min(
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ export default function SlotActivityOverlay({
|
|||||||
|
|
||||||
// Durations
|
// Durations
|
||||||
const LOOP_MS = 2 * 60 * 1000; // ~2 min
|
const LOOP_MS = 2 * 60 * 1000; // ~2 min
|
||||||
const TDR_MS = 30 * 1000; // ~30 s
|
const TDR_MS = 110 * 1000; // ~2 min laut die Eigaben
|
||||||
const ALIGN_MS = 10 * 60 * 1000; // ~10 min
|
const ALIGN_MS = 10 * 60 * 1000; // ~10 min
|
||||||
|
|
||||||
if (!loopActive && !tdrActive && !comparisonActive) return null;
|
if (!loopActive && !tdrActive && !comparisonActive) return null;
|
||||||
|
|||||||
@@ -69,14 +69,20 @@ export default function KueModal({ showModal, onClose, slot }: KueModalProps) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="p-2 flex justify-between items-center rounded-t-md">
|
<div className="p-2 flex justify-between items-center rounded-t-md bg-surface-alt border-b border-base">
|
||||||
<h2 className="text-base font-bold">Einstellungen KÜ {slot + 1}</h2>
|
<h2 className="text-base font-bold text-fg">
|
||||||
<button onClick={onClose} className="text-2xl hover:text-gray-200">
|
Einstellungen KÜ {slot + 1}
|
||||||
|
</h2>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="text-2xl text-fg-muted hover:text-fg transition-colors focus:outline-none focus:ring-2 focus:ring-accent/50 rounded"
|
||||||
|
aria-label="Modal schließen"
|
||||||
|
>
|
||||||
<i className="bi bi-x-circle-fill"></i>
|
<i className="bi bi-x-circle-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex justify-start bg-gray-100 space-x-2 p-2">
|
<div className="flex justify-start bg-surface-alt space-x-2 p-2 border-b border-base">
|
||||||
{[
|
{[
|
||||||
{ label: "Allgemein", key: "kue" as const },
|
{ label: "Allgemein", key: "kue" as const },
|
||||||
{ label: "TDR ", key: "tdr" as const },
|
{ label: "TDR ", key: "tdr" as const },
|
||||||
@@ -86,18 +92,17 @@ export default function KueModal({ showModal, onClose, slot }: KueModalProps) {
|
|||||||
<button
|
<button
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => setActiveTab(key)}
|
onClick={() => setActiveTab(key)}
|
||||||
className={`px-4 py-1 rounded-t font-bold text-sm ${
|
className={`px-4 py-1 rounded-t font-semibold text-sm transition-colors focus:outline-none focus:ring-2 focus:ring-accent/40 ${
|
||||||
activeTab === key
|
activeTab === key
|
||||||
? "bg-white text-littwin-blue"
|
? "bg-surface text-accent shadow-sm"
|
||||||
: "text-gray-500 hover:text-black"
|
: "text-fg-muted hover:text-fg"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="p-4 bg-surface rounded-b-md h-[20rem] laptop:h-[24rem] 2xl:h-[30rem] overflow-y-auto text-fg">
|
||||||
<div className="p-4 bg-white rounded-b-md h-[20rem] laptop:h-[24rem] 2xl:h-[30rem] overflow-y-auto">
|
|
||||||
{activeTab === "kue" && (
|
{activeTab === "kue" && (
|
||||||
<KueEinstellung
|
<KueEinstellung
|
||||||
slot={slot}
|
slot={slot}
|
||||||
|
|||||||
@@ -14,22 +14,22 @@ export default function MeldungenTabelle({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="overflow-auto max-h-[80vh]">
|
<div className="overflow-auto max-h-[80vh]">
|
||||||
<table className="min-w-full border">
|
<table className="min-w-full border border-base table-surface text-fg">
|
||||||
<thead className="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 text-left sticky top-0 z-10">
|
<thead className="text-left sticky top-0 z-10 bg-surface-alt/90 backdrop-blur supports-[backdrop-filter]:bg-surface-alt/70">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-base bg-surface-alt text-fg font-medium">
|
||||||
Prio
|
Prio
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-base bg-surface-alt text-fg font-medium">
|
||||||
Zeitstempel
|
Zeitstempel
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-base bg-surface-alt text-fg font-medium">
|
||||||
Quelle
|
Quelle
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-base bg-surface-alt text-fg font-medium">
|
||||||
Meldung
|
Meldung
|
||||||
</th>
|
</th>
|
||||||
<th className="p-2 border bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<th className="p-2 border border-base bg-surface-alt text-fg font-medium">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -38,15 +38,15 @@ export default function MeldungenTabelle({
|
|||||||
{messages.map((msg, index) => (
|
{messages.map((msg, index) => (
|
||||||
<tr
|
<tr
|
||||||
key={index}
|
key={index}
|
||||||
className="hover:bg-gray-100 dark:hover:bg-gray-700"
|
className="transition-colors hover:bg-surface-alt/60"
|
||||||
>
|
>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border border-base p-2 bg-surface text-fg">
|
||||||
<div
|
<div
|
||||||
className="w-4 h-4 rounded"
|
className="w-4 h-4 rounded"
|
||||||
style={{ backgroundColor: msg.c }}
|
style={{ backgroundColor: msg.c }}
|
||||||
></div>
|
></div>
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border border-base p-2 bg-surface text-fg">
|
||||||
{new Date(msg.t).toLocaleString("de-DE", {
|
{new Date(msg.t).toLocaleString("de-DE", {
|
||||||
day: "2-digit",
|
day: "2-digit",
|
||||||
month: "2-digit",
|
month: "2-digit",
|
||||||
@@ -56,13 +56,13 @@ export default function MeldungenTabelle({
|
|||||||
second: "2-digit",
|
second: "2-digit",
|
||||||
})}
|
})}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border border-base p-2 bg-surface text-fg">
|
||||||
{msg.i}
|
{msg.i}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border border-base p-2 bg-surface text-fg">
|
||||||
{msg.m}
|
{msg.m}
|
||||||
</td>
|
</td>
|
||||||
<td className="border p-2 bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
|
<td className="border border-base p-2 bg-surface text-fg">
|
||||||
{msg.v}
|
{msg.v}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -70,7 +70,7 @@ export default function MeldungenTabelle({
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{messages.length === 0 && (
|
{messages.length === 0 && (
|
||||||
<div className="mt-4 text-center text-gray-500 italic">
|
<div className="mt-4 text-center text-fg-muted italic">
|
||||||
Keine Meldungen im gewählten Zeitraum vorhanden.
|
Keine Meldungen im gewählten Zeitraum vorhanden.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -60,14 +60,14 @@ export default function MeldungenView() {
|
|||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={() => dispatch(getMessagesThunk({ fromDate, toDate }))}
|
onClick={() => dispatch(getMessagesThunk({ fromDate, toDate }))}
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded h-fit"
|
className="btn-primary px-4 py-2 h-fit"
|
||||||
>
|
>
|
||||||
Anzeigen
|
Anzeigen
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Listbox value={sourceFilter} onChange={setSourceFilter}>
|
<Listbox value={sourceFilter} onChange={setSourceFilter}>
|
||||||
<div className="relative ml-6 w-64">
|
<div className="relative ml-6 w-64">
|
||||||
<Listbox.Button className="bg-white text-gray-900 w-full border px-4 py-2 rounded text-left flex justify-between items-center dark:bg-gray-900 dark:text-gray-100">
|
<Listbox.Button className="bg-[var(--color-surface)] text-[var(--color-fg)] w-full border border-base px-4 py-2 rounded text-left flex justify-between items-center">
|
||||||
<span>{sourceFilter}</span>
|
<span>{sourceFilter}</span>
|
||||||
<svg
|
<svg
|
||||||
className="w-5 h-5 text-gray-400"
|
className="w-5 h-5 text-gray-400"
|
||||||
@@ -83,19 +83,19 @@ export default function MeldungenView() {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Listbox.Button>
|
</Listbox.Button>
|
||||||
<Listbox.Options className="bg-white absolute z-50 mt-1 w-full border rounded dark:bg-gray-900">
|
<Listbox.Options className="bg-[var(--color-surface)] absolute z-50 mt-1 w-full border border-base rounded shadow-sm">
|
||||||
{sources.map((src) => (
|
{sources.map((src) => (
|
||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={src}
|
key={src}
|
||||||
value={src}
|
value={src}
|
||||||
className={({ selected, active, disabled }) =>
|
className={({ selected, active, disabled }) =>
|
||||||
`px-4 py-2 cursor-pointer text-gray-900 dark:text-gray-100 ${
|
`px-4 py-2 cursor-pointer text-[var(--color-fg)] transition-colors ${
|
||||||
selected
|
disabled
|
||||||
? "bg-littwin-blue text-white"
|
? "opacity-50 text-[var(--color-muted)] cursor-not-allowed"
|
||||||
|
: selected
|
||||||
|
? "bg-accent text-white"
|
||||||
: active
|
: active
|
||||||
? "bg-blue-100 dark:bg-gray-700 dark:text-white"
|
? "bg-[var(--color-surface-alt)]"
|
||||||
: disabled
|
|
||||||
? "opacity-50 text-gray-400 dark:text-gray-500 cursor-not-allowed"
|
|
||||||
: ""
|
: ""
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ import { useAdminAuth } from "./hooks/useAdminAuth";
|
|||||||
const DatabaseSettings: React.FC = () => {
|
const DatabaseSettings: React.FC = () => {
|
||||||
const { isAdminLoggedIn } = useAdminAuth(true);
|
const { isAdminLoggedIn } = useAdminAuth(true);
|
||||||
return (
|
return (
|
||||||
<div className="p-6 bg-gray-100 dark:bg-gray-800 max-w-5xl mr-auto rounded shadow text-gray-900 dark:text-gray-100">
|
<div className="p-6 bg-[var(--color-surface-alt)] max-w-5xl mr-auto rounded shadow text-[var(--color-fg)]">
|
||||||
<h2 className="text-lg font-bold mb-6">Datenbank Einstellungen</h2>
|
<h2 className="text-lg font-bold mb-6">Datenbank Einstellungen</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClearMessages}
|
onClick={handleClearMessages}
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded shadow "
|
className="btn-accent px-4 py-2 rounded shadow"
|
||||||
>
|
>
|
||||||
Meldungen löschen
|
Meldungen löschen
|
||||||
</button>
|
</button>
|
||||||
@@ -25,7 +25,7 @@ const DatabaseSettings: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClearLogger}
|
onClick={handleClearLogger}
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded shadow "
|
className="btn-accent px-4 py-2 rounded shadow"
|
||||||
>
|
>
|
||||||
Messwerte Logger löschen
|
Messwerte Logger löschen
|
||||||
</button>
|
</button>
|
||||||
@@ -41,7 +41,7 @@ const DatabaseSettings: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClearDatabase}
|
onClick={handleClearDatabase}
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded shadow "
|
className="btn-accent px-4 py-2 rounded shadow"
|
||||||
>
|
>
|
||||||
Datenbank vollständig leeren
|
Datenbank vollständig leeren
|
||||||
</button>
|
</button>
|
||||||
@@ -49,7 +49,7 @@ const DatabaseSettings: React.FC = () => {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handleClearConfig}
|
onClick={handleClearConfig}
|
||||||
className="bg-littwin-blue text-white px-4 py-2 rounded shadow "
|
className="btn-accent px-4 py-2 rounded shadow"
|
||||||
>
|
>
|
||||||
Konfiguration löschen
|
Konfiguration löschen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -63,8 +63,11 @@ const GeneralSettings: React.FC = () => {
|
|||||||
setMac1(systemSettings.mac1 || "");
|
setMac1(systemSettings.mac1 || "");
|
||||||
}, [systemSettings]);
|
}, [systemSettings]);
|
||||||
|
|
||||||
|
const inputCls =
|
||||||
|
"border border-base focus:border-accent rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 md:p-3 bg-gray-100 dark:bg-gray-800 max-w-5xl mr-auto overflow-y-auto max-h-[calc(100vh-200px)] dark:text-gray-100 ">
|
<div className="p-6 md:p-3 bg-[var(--color-surface-alt)] max-w-5xl mr-auto overflow-y-auto max-h-[calc(100vh-200px)] text-[var(--color-fg)]">
|
||||||
<h2 className="text-sm md:text-md font-bold mb-2">
|
<h2 className="text-sm md:text-md font-bold mb-2">
|
||||||
Allgemeine Einstellungen
|
Allgemeine Einstellungen
|
||||||
</h2>
|
</h2>
|
||||||
@@ -74,25 +77,18 @@ const GeneralSettings: React.FC = () => {
|
|||||||
<label className="block text-xs md:text-sm font-medium">Name:</label>
|
<label className="block text-xs md:text-sm font-medium">Name:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className={inputCls}
|
||||||
value={name}
|
value={name}
|
||||||
onChange={(e) => setName(e.target.value)}
|
onChange={(e) => setName(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* MAC Adresse */}
|
{/* MAC Adresse */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs md:text-sm font-medium">
|
<label className="block text-xs md:text-sm font-medium">
|
||||||
MAC Adresse 1:
|
MAC Adresse 1:
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input type="text" className={inputCls} value={mac1} disabled />
|
||||||
type="text"
|
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
|
||||||
value={mac1}
|
|
||||||
disabled
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Systemzeit */}
|
{/* Systemzeit */}
|
||||||
<div className="col-span-2">
|
<div className="col-span-2">
|
||||||
<label className="block text-xs md:text-sm font-medium mb-1">
|
<label className="block text-xs md:text-sm font-medium mb-1">
|
||||||
@@ -101,13 +97,13 @@ const GeneralSettings: React.FC = () => {
|
|||||||
<div className="flex flex-row gap-2">
|
<div className="flex flex-row gap-2">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className={inputCls}
|
||||||
value={systemUhr.replace(/\s*Uhr$/, "")}
|
value={systemUhr.replace(/\s*Uhr$/, "")}
|
||||||
disabled
|
disabled
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
className="btn-accent px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
onClick={() => handleSetDateTime()}
|
onClick={() => handleSetDateTime()}
|
||||||
>
|
>
|
||||||
Systemzeit übernehmen
|
Systemzeit übernehmen
|
||||||
@@ -120,7 +116,7 @@ const GeneralSettings: React.FC = () => {
|
|||||||
<label className="block text-xs md:text-sm font-medium">IP:</label>
|
<label className="block text-xs md:text-sm font-medium">IP:</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className={inputCls}
|
||||||
value={ip}
|
value={ip}
|
||||||
onChange={(e) => setIp(e.target.value)}
|
onChange={(e) => setIp(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -131,7 +127,7 @@ const GeneralSettings: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className={inputCls}
|
||||||
value={subnet}
|
value={subnet}
|
||||||
onChange={(e) => setSubnet(e.target.value)}
|
onChange={(e) => setSubnet(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -142,7 +138,7 @@ const GeneralSettings: React.FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-600 focus:border-littwin-blue dark:focus:border-littwin-blue rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className={inputCls}
|
||||||
value={gateway}
|
value={gateway}
|
||||||
onChange={(e) => setGateway(e.target.value)}
|
onChange={(e) => setGateway(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -152,7 +148,7 @@ const GeneralSettings: React.FC = () => {
|
|||||||
<div className="col-span-2 flex flex-wrap md:justify-between gap-1 mt-2">
|
<div className="col-span-2 flex flex-wrap md:justify-between gap-1 mt-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
className="btn-accent px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
onClick={() => handleReboot()}
|
onClick={() => handleReboot()}
|
||||||
>
|
>
|
||||||
Neustart CPL
|
Neustart CPL
|
||||||
@@ -160,7 +156,7 @@ const GeneralSettings: React.FC = () => {
|
|||||||
{isAdminLoggedIn && (
|
{isAdminLoggedIn && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
className="btn-accent px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const confirmed = window.confirm(
|
const confirmed = window.confirm(
|
||||||
"⚠️ Wollen Sie wirklich ein Firmwareupdate für alle KÜ-Module starten?"
|
"⚠️ Wollen Sie wirklich ein Firmwareupdate für alle KÜ-Module starten?"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const NTPSettings: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 md:p-3 bg-gray-100 dark:bg-gray-800 max-w-5xl mr-auto text-gray-900 dark:text-gray-100">
|
<div className="p-6 md:p-3 bg-[var(--color-surface-alt)] max-w-5xl mr-auto text-[var(--color-fg)]">
|
||||||
<h2 className="text-sm md:text-md font-bold mb-4">NTP Einstellungen</h2>
|
<h2 className="text-sm md:text-md font-bold mb-4">NTP Einstellungen</h2>
|
||||||
|
|
||||||
<div className="grid md:grid-cols-2 gap-3">
|
<div className="grid md:grid-cols-2 gap-3">
|
||||||
@@ -35,7 +35,7 @@ const NTPSettings: React.FC = () => {
|
|||||||
<label className="block text-xs font-medium">NTP Server 1</label>
|
<label className="block text-xs font-medium">NTP Server 1</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={ntp1}
|
value={ntp1}
|
||||||
onChange={(e) => setNtp1(e.target.value)}
|
onChange={(e) => setNtp1(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -45,7 +45,7 @@ const NTPSettings: React.FC = () => {
|
|||||||
<label className="block text-xs font-medium">NTP Server 2</label>
|
<label className="block text-xs font-medium">NTP Server 2</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={ntp2}
|
value={ntp2}
|
||||||
onChange={(e) => setNtp2(e.target.value)}
|
onChange={(e) => setNtp2(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -55,7 +55,7 @@ const NTPSettings: React.FC = () => {
|
|||||||
<label className="block text-xs font-medium">NTP Server 3</label>
|
<label className="block text-xs font-medium">NTP Server 3</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={ntp3}
|
value={ntp3}
|
||||||
onChange={(e) => setNtp3(e.target.value)}
|
onChange={(e) => setNtp3(e.target.value)}
|
||||||
/>
|
/>
|
||||||
@@ -65,7 +65,7 @@ const NTPSettings: React.FC = () => {
|
|||||||
<label className="block text-xs font-medium">Zeitzone</label>
|
<label className="block text-xs font-medium">Zeitzone</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={ntpTimezone}
|
value={ntpTimezone}
|
||||||
onChange={(e) => setNtpTimezone(e.target.value)}
|
onChange={(e) => setNtpTimezone(e.target.value)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 md:p-3 bg-gray-100 dark:bg-gray-800 max-w-5xl mr-auto text-gray-900 dark:text-gray-100 ">
|
<div className="p-6 md:p-3 bg-[var(--color-surface-alt)] max-w-5xl mr-auto text-[var(--color-fg)]">
|
||||||
<div className="flex justify-between items-center mb-3">
|
<div className="flex justify-between items-center mb-3">
|
||||||
<Image
|
<Image
|
||||||
src="/images/OPCUA.jpg"
|
src="/images/OPCUA.jpg"
|
||||||
@@ -44,9 +44,11 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
<label className="mr-3 font-medium text-sm">Server Status:</label>
|
<label className="mr-3 font-medium text-sm">Server Status:</label>
|
||||||
<button
|
<button
|
||||||
onClick={() => dispatch(toggleOpcUaServer())}
|
onClick={() => dispatch(toggleOpcUaServer())}
|
||||||
className={`px-3 py-1 rounded text-sm ${
|
className={`px-3 py-1 rounded text-sm font-medium transition-colors text-white ${
|
||||||
opcuaSettings.isEnabled ? "bg-littwin-blue" : "bg-gray-300"
|
opcuaSettings.isEnabled
|
||||||
} text-white`}
|
? "bg-accent hover:brightness-110"
|
||||||
|
: "bg-[var(--color-muted)] hover:bg-[var(--color-fg)]/20"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{opcuaSettings.isEnabled ? "Aktiviert" : "Deaktiviert"}
|
{opcuaSettings.isEnabled ? "Aktiviert" : "Deaktiviert"}
|
||||||
</button>
|
</button>
|
||||||
@@ -62,7 +64,7 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
<select
|
<select
|
||||||
value={opcuaSettings.encryption}
|
value={opcuaSettings.encryption}
|
||||||
onChange={(e) => dispatch(setOpcUaEncryption(e.target.value))}
|
onChange={(e) => dispatch(setOpcUaEncryption(e.target.value))}
|
||||||
className="w-full p-1 border border-gray-300 rounded-md text-sm"
|
className="w-full p-1 border border-base rounded-md text-sm bg-[var(--color-surface)] text-[var(--color-fg)]"
|
||||||
>
|
>
|
||||||
<option value="None">Keine</option>
|
<option value="None">Keine</option>
|
||||||
<option value="Basic256">Basic256</option>
|
<option value="Basic256">Basic256</option>
|
||||||
@@ -74,7 +76,7 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
{/* ✅ OPCUA Zustand */}
|
{/* ✅ OPCUA Zustand */}
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="block font-medium text-sm mb-1">OPCUA Zustand</label>
|
<label className="block font-medium text-sm mb-1">OPCUA Zustand</label>
|
||||||
<div className="p-1 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-sm text-gray-900 dark:text-gray-100">
|
<div className="p-1 border border-base rounded-md bg-[var(--color-surface)] text-sm text-[var(--color-fg)]">
|
||||||
{opcuaSettings.opcUaZustand}
|
{opcuaSettings.opcUaZustand}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -85,7 +87,7 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
<div className="flex">
|
<div className="flex">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="flex-grow p-1 border border-gray-300 dark:border-gray-700 rounded-l-md text-sm !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="flex-grow p-1 border border-base rounded-l-md text-sm bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={nodesetName}
|
value={nodesetName}
|
||||||
onChange={(e) => setNodesetName(e.target.value)}
|
onChange={(e) => setNodesetName(e.target.value)}
|
||||||
disabled={opcuaSettings.isEnabled} // Disable input when server is enabled
|
disabled={opcuaSettings.isEnabled} // Disable input when server is enabled
|
||||||
@@ -106,7 +108,7 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
<label className="block font-medium text-sm mb-1">
|
<label className="block font-medium text-sm mb-1">
|
||||||
Aktuelle OPC-Clients
|
Aktuelle OPC-Clients
|
||||||
</label>
|
</label>
|
||||||
<div className="p-1 border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-900 text-sm text-gray-900 dark:text-gray-100">
|
<div className="p-1 border border-base rounded-md bg-[var(--color-surface)] text-sm text-[var(--color-fg)]">
|
||||||
{opcUaActiveClientCount}
|
{opcUaActiveClientCount}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -120,12 +122,12 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
{opcuaSettings.users.map((user) => (
|
{opcuaSettings.users.map((user) => (
|
||||||
<li
|
<li
|
||||||
key={user.id}
|
key={user.id}
|
||||||
className="p-1 bg-white shadow-sm rounded-md flex justify-between items-center text-sm"
|
className="p-1 bg-[var(--color-surface)] border border-base rounded-md flex justify-between items-center text-sm text-[var(--color-fg)]"
|
||||||
>
|
>
|
||||||
<span className="font-medium">{user.username}</span>
|
<span className="font-medium">{user.username}</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => dispatch(removeOpcUaUser(user.id))}
|
onClick={() => dispatch(removeOpcUaUser(user.id))}
|
||||||
className="text-red-500"
|
className="text-danger hover:underline"
|
||||||
>
|
>
|
||||||
Löschen
|
Löschen
|
||||||
</button>
|
</button>
|
||||||
@@ -140,18 +142,18 @@ export default function OPCUAInterfaceSettings() {
|
|||||||
placeholder="Benutzername"
|
placeholder="Benutzername"
|
||||||
value={newUsername}
|
value={newUsername}
|
||||||
onChange={(e) => setNewUsername(e.target.value)}
|
onChange={(e) => setNewUsername(e.target.value)}
|
||||||
className="p-1 border rounded flex-grow text-sm"
|
className="p-1 border border-base rounded flex-grow text-sm bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] focus:outline-none focus:border-accent"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Passwort"
|
placeholder="Passwort"
|
||||||
value={newPassword}
|
value={newPassword}
|
||||||
onChange={(e) => setNewPassword(e.target.value)}
|
onChange={(e) => setNewPassword(e.target.value)}
|
||||||
className="p-1 border rounded flex-grow text-sm"
|
className="p-1 border border-base rounded flex-grow text-sm bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] focus:outline-none focus:border-accent"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleAddUser}
|
onClick={handleAddUser}
|
||||||
className="bg-littwin-blue text-white p-1 rounded text-sm"
|
className="btn-primary p-1 rounded text-sm"
|
||||||
>
|
>
|
||||||
Hinzufügen
|
Hinzufügen
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const UserManagementSettings: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6 md:p-3 bg-gray-100 dark:bg-gray-800 max-w-5xl mr-auto text-gray-900 dark:text-gray-100">
|
<div className="p-6 md:p-3 bg-[var(--color-surface-alt)] max-w-5xl mr-auto text-[var(--color-fg)]">
|
||||||
<h2 className="text-sm md:text-md font-bold mb-4">Login Admin-Bereich</h2>
|
<h2 className="text-sm md:text-md font-bold mb-4">Login Admin-Bereich</h2>
|
||||||
|
|
||||||
{/* Admin Login/Logout */}
|
{/* Admin Login/Logout */}
|
||||||
@@ -52,7 +52,7 @@ const UserManagementSettings: React.FC = () => {
|
|||||||
{isAdminLoggedIn ? (
|
{isAdminLoggedIn ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
className="btn-accent px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
try {
|
try {
|
||||||
localStorage.removeItem("adminLoginTime");
|
localStorage.removeItem("adminLoginTime");
|
||||||
@@ -70,7 +70,7 @@ const UserManagementSettings: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Benutzername"
|
placeholder="Benutzername"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={username}
|
value={username}
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
@@ -78,14 +78,14 @@ const UserManagementSettings: React.FC = () => {
|
|||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Passwort"
|
placeholder="Passwort"
|
||||||
className="border border-gray-300 dark:border-gray-700 rounded h-8 p-1 w-full text-xs !bg-white !text-black placeholder-gray-400 transition-colors duration-150 focus:outline-none dark:bg-gray-900 dark:text-gray-100 dark:placeholder-gray-500"
|
className="border border-base rounded h-8 p-1 w-full text-xs bg-[var(--color-surface)] text-[var(--color-fg)] placeholder-[var(--color-muted)] transition-colors duration-150 focus:outline-none"
|
||||||
value={password}
|
value={password}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="bg-littwin-blue text-white px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
className="btn-accent px-4 py-2 h-8 text-xs rounded whitespace-nowrap"
|
||||||
onClick={handleLogin}
|
onClick={handleLogin}
|
||||||
>
|
>
|
||||||
Admin anmelden
|
Admin anmelden
|
||||||
@@ -96,9 +96,9 @@ const UserManagementSettings: React.FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loginSuccess && (
|
{loginSuccess && (
|
||||||
<p className="text-green-600 text-xs mt-2">Login erfolgreich!</p>
|
<p className="text-success text-xs mt-2">Login erfolgreich!</p>
|
||||||
)}
|
)}
|
||||||
{error && <p className="text-red-500 text-xs mt-2">{error}</p>}
|
{error && <p className="text-danger text-xs mt-2">{error}</p>}
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
// Benutzerverwaltungstabelle (kommt später)
|
// Benutzerverwaltungstabelle (kommt später)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const ProgressModal: React.FC<Props> = ({ visible, progress, slot }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 ">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 ">
|
||||||
<div className="bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 p-6 rounded shadow-md text-center w-80">
|
<div className="p-6 rounded shadow-sm text-center w-80 bg-[var(--color-surface)] dark:bg-[var(--color-surface)] text-[var(--color-fg)] border border-[var(--color-border)]">
|
||||||
{/*
|
{/*
|
||||||
<h2 className="text-lg font-bold mb-4">
|
<h2 className="text-lg font-bold mb-4">
|
||||||
Firmwareupdate
|
Firmwareupdate
|
||||||
@@ -26,9 +26,9 @@ const ProgressModal: React.FC<Props> = ({ visible, progress, slot }) => {
|
|||||||
</h2>
|
</h2>
|
||||||
Bitte Fenster nicht schließen
|
Bitte Fenster nicht schließen
|
||||||
<h2></h2>
|
<h2></h2>
|
||||||
<div className="w-full bg-gray-200 rounded-full h-4">
|
<div className="w-full h-4 rounded-full bg-[var(--color-surface-alt)]/80 dark:bg-[var(--color-surface-alt)]/30 border border-[var(--color-border)] overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="bg-littwin-blue h-4 rounded-full transition-all duration-100"
|
className="h-full rounded-full transition-all duration-200 bg-[var(--color-accent)]"
|
||||||
style={{ width: `${progress}%` }}
|
style={{ width: `${progress}%` }}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ export const DetailModal = ({
|
|||||||
<div className="absolute top-0 right-0 flex gap-3">
|
<div className="absolute top-0 right-0 flex gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={toggleFullScreen}
|
onClick={toggleFullScreen}
|
||||||
className="text-2xl text-gray-600 hover:text-gray-800"
|
className="text-2xl text-[var(--color-fg-muted)] hover:text-[var(--color-fg)] transition"
|
||||||
>
|
>
|
||||||
<i
|
<i
|
||||||
className={
|
className={
|
||||||
@@ -467,7 +467,7 @@ export const DetailModal = ({
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
className="text-2xl text-gray-600 hover:text-gray-800"
|
className="text-2xl text-[var(--color-fg-muted)] hover:text-[var(--color-danger)] transition"
|
||||||
>
|
>
|
||||||
<i className="bi bi-x-circle-fill"></i>
|
<i className="bi bi-x-circle-fill"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -481,7 +481,7 @@ export const DetailModal = ({
|
|||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="h-[85%] bg-white dark:bg-gray-800 rounded shadow border border-gray-200 dark:border-gray-700 p-2">
|
<div className="h-[85%] rounded shadow border p-2 bg-[var(--color-surface)] border-[var(--color-border)]">
|
||||||
<Line ref={chartRef} data={chartData} options={chartOptions} />
|
<Line ref={chartRef} data={chartData} options={chartOptions} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const SystemOverviewGrid = ({ voltages, onOpenDetail }: Props) => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={key}
|
key={key}
|
||||||
className="p-4 border rounded shadow bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-gray-900 dark:text-gray-100"
|
className="p-4 border rounded shadow-sm bg-[var(--color-surface)] dark:bg-[var(--color-surface)] border-[var(--color-border)] text-[var(--color-fg)] hover:bg-[var(--color-surface-alt)]/60 dark:hover:bg-[var(--color-surface-alt)]/30 transition"
|
||||||
>
|
>
|
||||||
<h2 className="font-semibold">{key}</h2>
|
<h2 className="font-semibold">{key}</h2>
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
@@ -71,8 +71,8 @@ const SystemPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-4 bg-white dark:bg-gray-900">
|
<div className="p-4 bg-[var(--color-background)] text-[var(--color-fg)]">
|
||||||
<h1 className="text-xl font-bold mb-4">
|
<h1 className="text-xl font-bold mb-4 tracking-wide">
|
||||||
System Spannungen & Temperaturen
|
System Spannungen & Temperaturen
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ const SystemPage = () => {
|
|||||||
<div className="flex justify-center items-center h-[400px]">
|
<div className="flex justify-center items-center h-[400px]">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<ClipLoader size={50} color="#3B82F6" />
|
<ClipLoader size={50} color="#3B82F6" />
|
||||||
<p className="mt-4 text-gray-500">
|
<p className="mt-4 text-[var(--color-fg-muted)]">
|
||||||
Lade Systemdaten … bitte warten
|
Lade Systemdaten … bitte warten
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,12 +36,12 @@ const Navigation: React.FC<NavigationProps> = ({ className }) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="bg-white dark:bg-gray-900 h-full">
|
<aside className="h-full bg-[var(--color-surface)] dark:bg-[var(--color-surface)] border-r border-[var(--color-border)]">
|
||||||
<nav className={`h-full flex-shrink-0 mt-16 ${className || "w-48"}`}>
|
<nav className={`h-full flex-shrink-0 mt-16 ${className || "w-48"}`}>
|
||||||
{menuItems.map((item) => (
|
{menuItems.map((item) => (
|
||||||
<div key={item.name}>
|
<div key={item.name}>
|
||||||
{item.disabled ? (
|
{item.disabled ? (
|
||||||
<div className="block px-4 py-2 mb-4 font-bold whitespace-nowrap text-gray-400 dark:text-gray-600 cursor-not-allowed text-[1rem] sm:text-[1rem] md:text-[1rem] lg:text-[1rem] xl:text-sm 2xl:text-lg">
|
<div className="block px-4 py-2 mb-4 font-bold whitespace-nowrap text-[var(--color-fg-muted)] opacity-60 cursor-not-allowed text-[1rem] sm:text-[1rem] md:text-[1rem] lg:text-[1rem] xl:text-sm 2xl:text-lg">
|
||||||
{item.name}
|
{item.name}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -49,11 +49,13 @@ const Navigation: React.FC<NavigationProps> = ({ className }) => {
|
|||||||
href={formatPath(item.path)}
|
href={formatPath(item.path)}
|
||||||
prefetch={false}
|
prefetch={false}
|
||||||
onClick={() => setActiveLink(item.path)}
|
onClick={() => setActiveLink(item.path)}
|
||||||
className={`block px-4 py-2 mb-4 font-bold whitespace-nowrap transition duration-300 text-[1rem] sm:text-[1rem] md:text-[1rem] lg:text-[1rem] xl:text-sm 2xl:text-lg ${
|
className={`block px-4 py-2 mb-4 font-semibold whitespace-nowrap transition duration-200 rounded-r-full pr-6 relative text-[1rem] sm:text-[1rem] md:text-[1rem] lg:text-[1rem] xl:text-sm 2xl:text-lg
|
||||||
|
${
|
||||||
activeLink.startsWith(item.path)
|
activeLink.startsWith(item.path)
|
||||||
? "bg-sky-500 text-white rounded-r-full xl:mr-4 xl:w-full dark:bg-sky-600 dark:text-white"
|
? "bg-[var(--color-accent)] text-white shadow-sm xl:mr-4 xl:w-full"
|
||||||
: "text-black hover:bg-gray-200 rounded-r-full dark:text-gray-200 dark:hover:bg-gray-800"
|
: "text-[var(--color-fg-muted)] hover:text-[var(--color-fg)] hover:bg-[var(--color-surface-alt)]/80 dark:hover:bg-[var(--color-surface-alt)]/40"
|
||||||
}`}
|
}
|
||||||
|
`}
|
||||||
>
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Link>
|
</Link>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"win_da_state": [
|
"win_da_state": [
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
0,
|
||||||
1
|
1
|
||||||
],
|
],
|
||||||
"win_da_bezeichnung": [
|
"win_da_bezeichnung": [
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.869",
|
"version": "1.6.879",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.869",
|
"version": "1.6.879",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/roboto": "^5.1.0",
|
"@fontsource/roboto": "^5.1.0",
|
||||||
"@headlessui/react": "^2.2.4",
|
"@headlessui/react": "^2.2.4",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cpl-v4",
|
"name": "cpl-v4",
|
||||||
"version": "1.6.869",
|
"version": "1.6.879",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3000",
|
"dev": "next dev -p 3000",
|
||||||
|
|||||||
@@ -148,11 +148,11 @@ function AppContent({
|
|||||||
}, [pathname, dispatch]);
|
}, [pathname, dispatch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-screen overflow-hidden bg-gray-100 dark:bg-gray-900 text-gray-900 dark:text-gray-100">
|
<div className="flex flex-col h-screen overflow-hidden bg-[var(--color-background)] text-[var(--color-fg)]">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="flex flex-grow w-full">
|
<div className="flex flex-grow w-full">
|
||||||
<Navigation className="w-56" />
|
<Navigation className="w-56" />
|
||||||
<main className="w-full flex-grow bg-white dark:bg-gray-900">
|
<main className="w-full flex-grow bg-[var(--color-surface)] dark:bg-[var(--color-surface)] border-l border-[var(--color-border)]">
|
||||||
{sessionExpired && (
|
{sessionExpired && (
|
||||||
<div className="bg-red-500 text-white p-4 text-center">
|
<div className="bg-red-500 text-white p-4 text-center">
|
||||||
❌ Ihre Sitzung ist abgelaufen oder die Verbindung ist
|
❌ Ihre Sitzung ist abgelaufen oder die Verbindung ist
|
||||||
|
|||||||
@@ -8,6 +8,13 @@ export default function Document() {
|
|||||||
<link rel="icon" href="/favicon.png" type="image/png" />
|
<link rel="icon" href="/favicon.png" type="image/png" />
|
||||||
</Head>
|
</Head>
|
||||||
<body>
|
<body>
|
||||||
|
{/* Theme init (executed before React hydration) */}
|
||||||
|
<script
|
||||||
|
// eslint-disable-next-line react/no-danger
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `(() => { try { const ls = localStorage.getItem('theme'); const mql = window.matchMedia('(prefers-color-scheme: dark)'); const wantDark = ls === 'dark' || (!ls && mql.matches); if (wantDark) document.documentElement.classList.add('dark'); } catch(e) {} })();`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<Main /> {/* Hier wird der Seiteninhalt eingebettet */}
|
<Main /> {/* Hier wird der Seiteninhalt eingebettet */}
|
||||||
<NextScript /> {/* Fügt Next.js-Skripte für die Seite hinzu */}
|
<NextScript /> {/* Fügt Next.js-Skripte für die Seite hinzu */}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -3,45 +3,255 @@
|
|||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
/* Light theme tokens */
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #f1f5f9; /* slate-100 */
|
||||||
--foreground: #171717;
|
--foreground: #0f172a; /* slate-900 */
|
||||||
--littwin-blue: #00aeef;
|
--littwin-blue: #00aeef;
|
||||||
|
--color-background: #f1f5f9;
|
||||||
|
--color-surface: #ffffff;
|
||||||
|
--color-surface-alt: #f8fafc; /* slate-50 */
|
||||||
|
--color-border: #e2e8f0; /* slate-200 */
|
||||||
|
--color-fg: #0f172a; /* slate-900 */
|
||||||
|
--color-fg-muted: #475569; /* slate-600 */
|
||||||
|
/* Alias for convenience (used in components) */
|
||||||
|
--color-muted: var(--color-fg-muted);
|
||||||
|
--color-accent: #00aeef;
|
||||||
|
--color-accent-soft: #e0f7ff;
|
||||||
|
--color-danger: #dc2626; /* red-600 */
|
||||||
|
--color-warning: #f59e0b; /* amber-500 */
|
||||||
|
--color-success: #16a34a; /* green-600 */
|
||||||
|
--color-info: #0ea5e9; /* sky-500 */
|
||||||
|
--color-ring: #38bdf8; /* sky-400 */
|
||||||
|
--color-input-bg: #ffffff;
|
||||||
|
--color-input-border: #cbd5e1; /* slate-300 */
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
/* Dark theme overrides (activated via .dark class on <html>) */
|
||||||
:root {
|
.dark {
|
||||||
--background: #0a0a0a;
|
--background: #0f1115;
|
||||||
--foreground: #ededed;
|
--foreground: #e2e8f0;
|
||||||
}
|
--color-background: #0f1115; /* near slate-950 */
|
||||||
|
--color-surface: #1c232d; /* custom dark surface */
|
||||||
|
--color-surface-alt: #243140; /* slightly elevated */
|
||||||
|
--color-border: #334155; /* slate-600 */
|
||||||
|
--color-fg: #e2e8f0; /* slate-200 */
|
||||||
|
--color-fg-muted: #94a3b8; /* slate-400 */
|
||||||
|
--color-muted: var(--color-fg-muted);
|
||||||
|
--color-accent: #00aeef;
|
||||||
|
--color-accent-soft: #06394d; /* soft accent background */
|
||||||
|
--color-danger: #ef4444; /* red-500 */
|
||||||
|
--color-warning: #fbbf24; /* amber-400 */
|
||||||
|
--color-success: #22c55e; /* green-500 */
|
||||||
|
--color-info: #38bdf8; /* sky-400 */
|
||||||
|
--color-ring: #0ea5e9;
|
||||||
|
--color-input-bg: #1c232d;
|
||||||
|
--color-input-border: #475569;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: var(--foreground);
|
color: var(--color-fg);
|
||||||
background: var(--background);
|
background: var(--color-background);
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
accent-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smooth theme transitions (respect reduced motion) */
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
transition: background-color 0.25s ease, color 0.25s ease;
|
||||||
|
}
|
||||||
|
.theme-transition {
|
||||||
|
transition: background-color 0.25s ease, color 0.25s ease,
|
||||||
|
border-color 0.25s ease, box-shadow 0.25s ease;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.text-balance {
|
.text-balance {
|
||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
}
|
/* Semantic shortcut utilities */
|
||||||
|
.bg-background {
|
||||||
|
background-color: var(--color-background);
|
||||||
|
}
|
||||||
|
.bg-surface {
|
||||||
|
background-color: var(--color-surface);
|
||||||
|
}
|
||||||
|
.bg-surface-alt {
|
||||||
|
background-color: var(--color-surface-alt);
|
||||||
|
}
|
||||||
|
.text-fg {
|
||||||
|
color: var(--color-fg);
|
||||||
|
}
|
||||||
|
.text-fg-muted {
|
||||||
|
color: var(--color-fg-muted);
|
||||||
|
}
|
||||||
|
.text-muted {
|
||||||
|
color: var(--color-muted);
|
||||||
|
}
|
||||||
|
.border-base {
|
||||||
|
border-color: var(--color-border);
|
||||||
|
}
|
||||||
|
.ring-accent {
|
||||||
|
--tw-ring-color: var(--color-ring);
|
||||||
|
}
|
||||||
|
.bg-accent {
|
||||||
|
background-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
.bg-accent-soft {
|
||||||
|
background-color: var(--color-accent-soft);
|
||||||
|
}
|
||||||
|
.text-success {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
.text-danger {
|
||||||
|
color: var(--color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
/* Component abstractions (no @apply to avoid processing issues in global layer) */
|
||||||
input {
|
.card {
|
||||||
background-color: #333 !important; /* Dunkler Hintergrund im Darkmode */
|
background: var(--color-surface);
|
||||||
color: #fff !important; /* Weißer Text im Darkmode */
|
color: var(--color-fg);
|
||||||
border: 1px solid #555; /* Optional: etwas hellerer Rahmen */
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.375rem; /* rounded-md */
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
.card-elevated {
|
||||||
|
background: var(--color-surface);
|
||||||
|
color: var(--color-fg);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
box-shadow: 0 4px 10px -2px rgba(0, 0, 0, 0.25),
|
||||||
|
0 2px 4px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
.table-surface {
|
||||||
|
background: var(--color-surface);
|
||||||
|
color: var(--color-fg);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.table-head {
|
||||||
|
background: var(--color-surface-alt);
|
||||||
|
color: var(--color-fg);
|
||||||
|
font-weight: 500;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
.table-row-hover:hover {
|
||||||
|
background-color: color-mix(
|
||||||
|
in srgb,
|
||||||
|
var(--color-surface-alt) 85%,
|
||||||
|
transparent
|
||||||
|
);
|
||||||
|
}
|
||||||
|
.btn-accent {
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: filter 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
.btn-accent:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
.btn-accent:focus {
|
||||||
|
outline: 2px solid var(--color-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
/* Button Variants */
|
||||||
|
.btn-primary {
|
||||||
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
|
transition: background-color 0.2s ease, filter 0.2s ease;
|
||||||
|
}
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.btn-primary:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
.btn-primary:focus {
|
||||||
|
outline: 2px solid var(--color-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.btn-muted {
|
||||||
|
background: var(--color-muted);
|
||||||
|
color: var(--color-fg);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.btn-muted:hover {
|
||||||
|
background: var(--color-fg);
|
||||||
|
color: var(--color-background);
|
||||||
|
}
|
||||||
|
.btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-fg);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.btn-outline:hover {
|
||||||
|
background: var(--color-surface-alt);
|
||||||
|
}
|
||||||
|
.btn-danger {
|
||||||
|
background: var(--color-danger);
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.btn-danger:hover {
|
||||||
|
filter: brightness(1.1);
|
||||||
|
}
|
||||||
|
.input-base {
|
||||||
|
background: var(--color-input-bg);
|
||||||
|
color: var(--color-fg);
|
||||||
|
border: 1px solid var(--color-input-border);
|
||||||
|
border-radius: 0.375rem;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.input-base:focus {
|
||||||
|
border-color: var(--color-ring);
|
||||||
|
outline: 1px solid var(--color-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
.input-base:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
/* Form elements use tokens */
|
||||||
input {
|
input,
|
||||||
background-color: white !important; /* Heller Hintergrund im Lightmode */
|
select,
|
||||||
color: black !important; /* Schwarzer Text im Lightmode */
|
textarea {
|
||||||
border: 1px solid #ccc; /* Optional: heller Rahmen */
|
background-color: var(--color-input-bg) !important;
|
||||||
}
|
color: var(--color-fg) !important;
|
||||||
|
border: 1px solid var(--color-input-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: var(--color-muted);
|
||||||
|
opacity: 1; /* ensure consistent visibility */
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
|
outline: 2px solid var(--color-ring);
|
||||||
|
outline-offset: 2px;
|
||||||
|
border-color: var(--color-ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-datepicker__day--selected,
|
.react-datepicker__day--selected,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: "class",
|
// Ermöglicht zusätzlich zu .dark auch [data-theme="dark"] als Schalter
|
||||||
|
darkMode: ["class", '[data-theme="dark"]'],
|
||||||
content: [
|
content: [
|
||||||
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
|||||||
Reference in New Issue
Block a user