feat: ISO, RSL und TDR separate Charts ohne den Switcher

This commit is contained in:
ISA
2025-07-28 13:39:46 +02:00
parent 7a9fbc97dd
commit ce1dacda9b
15 changed files with 612 additions and 263 deletions

View File

@@ -6,6 +6,6 @@ NEXT_PUBLIC_USE_MOCK_BACKEND_LOOP_START=false
NEXT_PUBLIC_EXPORT_STATIC=false
NEXT_PUBLIC_USE_CGI=false
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.652
NEXT_PUBLIC_APP_VERSION=1.6.653
NEXT_PUBLIC_CPL_MODE=json # json (Entwicklungsumgebung) oder jsSimulatedProd (CPL ->CGI-Interface-Simulator) oder production (CPL-> CGI-Interface Platzhalter)

View File

@@ -5,5 +5,5 @@ NEXT_PUBLIC_CPL_API_PATH=/CPL
NEXT_PUBLIC_EXPORT_STATIC=true
NEXT_PUBLIC_USE_CGI=true
# App-Versionsnummer
NEXT_PUBLIC_APP_VERSION=1.6.652
NEXT_PUBLIC_APP_VERSION=1.6.653
NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,8 @@
## [1.6.653] 2025-07-28
- fix: KÜ slotnummer in der Messkurven Modal
---
## [1.6.652] 2025-07-28
- fix(Kue705FO): maintain consistent 3-line display layout

View File

@@ -1,25 +1,25 @@
"use client";
// /components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopChartActionBar.tsx
// /components/main/kabelueberwachung/kue705FO/Charts/IsoMeasurementChart/IsoChartActionBar.tsx
import React from "react";
import DateRangePicker from "@/components/common/DateRangePicker";
import { useDispatch, useSelector } from "react-redux";
import { RootState } from "@/redux/store";
import {
setLoopMeasurementCurveChartData,
setIsoMeasurementCurveChartData,
setSelectedMode,
setSelectedSlotType,
setChartOpen,
setLoading,
} from "@/redux/slices/kabelueberwachungChartSlice";
import { setBrushRange } from "@/redux/slices/brushSlice";
import { setChartTitle } from "@/redux/slices/loopChartTypeSlice";
import { setChartTitle } from "@/redux/slices/isoChartTypeSlice";
import { Listbox } from "@headlessui/react";
//-----------------------------------------------------------------------------------useLoopChartLoader
export const useLoopChartLoader = () => {
//-----------------------------------------------------------------------------------useIsoChartLoader
export const useIsoChartLoader = () => {
const dispatch = useDispatch();
const { vonDatum, bisDatum, selectedMode, selectedSlotType, slotNumber } =
useSelector((state: RootState) => state.kabelueberwachungChartSlice);
const { vonDatum, bisDatum, selectedMode, slotNumber } = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice
);
const hasShownNoDataAlert = React.useRef(false);
const formatDate = (dateString: string) => {
@@ -27,13 +27,9 @@ export const useLoopChartLoader = () => {
return `${year};${month};${day}`;
};
const getApiUrl = (
mode: "DIA0" | "DIA1" | "DIA2",
type: number,
slotNumber: number
) => {
const typeFolder =
type === 3 ? "isolationswiderstand" : "schleifenwiderstand";
const getApiUrl = (mode: "DIA0" | "DIA1" | "DIA2", slotNumber: number) => {
const type = 3; // Fest auf Isolationswiderstand gesetzt
const typeFolder = "isolationswiderstand";
let url: string;
@@ -45,23 +41,22 @@ export const useLoopChartLoader = () => {
)};${formatDate(bisDatum)};${slotNumber};${type};`;
}
console.log("API URL:", url); // Hier wird die URL in der Konsole ausgegeben
console.log("API URL:", url);
return url;
};
const loadLoopChartData = async () => {
const type = selectedSlotType === "schleifenwiderstand" ? 4 : 3;
const loadIsoChartData = async () => {
if (slotNumber === null) return;
dispatch(setLoading(true));
dispatch(setChartOpen(false));
dispatch(setLoopMeasurementCurveChartData([]));
dispatch(setIsoMeasurementCurveChartData([]));
const startTime = Date.now();
const MIN_LOADING_TIME_MS = 1000;
try {
const apiUrl = getApiUrl(selectedMode, type, slotNumber);
const apiUrl = getApiUrl(selectedMode, slotNumber);
const response = await fetch(apiUrl);
const data = await response.json();
@@ -72,14 +67,14 @@ export const useLoopChartLoader = () => {
await new Promise((res) => setTimeout(res, waitTime));
if (Array.isArray(data) && data.length > 0) {
dispatch(setLoopMeasurementCurveChartData(data));
dispatch(setIsoMeasurementCurveChartData(data));
dispatch(setChartOpen(true));
} else {
dispatch(setLoopMeasurementCurveChartData([]));
dispatch(setIsoMeasurementCurveChartData([]));
dispatch(setChartOpen(false));
if (!hasShownNoDataAlert.current) {
alert("⚠️ Keine Messdaten im gewählten Zeitraum gefunden");
hasShownNoDataAlert.current = true; // ⬅️ Nur einmal zeigen
hasShownNoDataAlert.current = true;
}
}
} catch (err) {
@@ -90,31 +85,24 @@ export const useLoopChartLoader = () => {
}
};
return { loadLoopChartData };
return { loadIsoChartData };
};
//-----------------------------------------------------------------------------------LoopChartActionBar
const LoopChartActionBar: React.FC = () => {
//-----------------------------------------------------------------------------------IsoChartActionBar
const IsoChartActionBar: React.FC = () => {
const dispatch = useDispatch();
const {
vonDatum,
bisDatum,
selectedMode,
selectedSlotType,
const { vonDatum, bisDatum, selectedMode, slotNumber, isLoading } =
useSelector((state: RootState) => state.kabelueberwachungChartSlice);
slotNumber,
const formatDate = (dateString: string) => {
const [year, month, day] = dateString.split("-");
return `${year};${month};${day}`;
};
isLoading,
} = useSelector((state: RootState) => state.kabelueberwachungChartSlice);
const getApiUrl = (
mode: "DIA0" | "DIA1" | "DIA2",
type: number,
slotNumber: number
) => {
const typeFolder =
type === 3 ? "isolationswiderstand" : "schleifenwiderstand";
const getApiUrl = (mode: "DIA0" | "DIA1" | "DIA2", slotNumber: number) => {
const type = 3; // Fest auf Isolationswiderstand gesetzt
const typeFolder = "isolationswiderstand";
const baseUrl =
process.env.NODE_ENV === "development"
@@ -122,30 +110,23 @@ const LoopChartActionBar: React.FC = () => {
: `${window.location.origin}/CPL?seite.ACP&${mode}=${formatDate(
vonDatum
)};${formatDate(bisDatum)};${slotNumber};${type};`;
console.log("baseUrl", baseUrl);
console.log("baseUrl", baseUrl);
return baseUrl;
};
const formatDate = (dateString: string) => {
const [year, month, day] = dateString.split("-");
return `${year};${month};${day}`;
};
const handleFetchData = async () => {
const type = selectedSlotType === "schleifenwiderstand" ? 4 : 3;
if (slotNumber === null) {
alert("⚠️ Bitte zuerst einen KÜ auswählen!");
return;
}
const apiUrl = getApiUrl(selectedMode, type, slotNumber);
const apiUrl = getApiUrl(selectedMode, slotNumber);
if (!apiUrl) return;
dispatch(setLoading(true));
dispatch(setChartOpen(false));
dispatch(setLoopMeasurementCurveChartData([]));
dispatch(setIsoMeasurementCurveChartData([]));
const MIN_LOADING_TIME_MS = 1000;
const startTime = Date.now();
@@ -162,24 +143,21 @@ const LoopChartActionBar: React.FC = () => {
const elapsedTime = Date.now() - startTime;
const waitTime = Math.max(0, MIN_LOADING_TIME_MS - elapsedTime);
await new Promise((resolve) => setTimeout(resolve, waitTime));
//------------------------
console.log("▶️ Lade Daten für:");
console.log("▶️ Lade Isolationswiderstand-Daten für:");
console.log(" Slot:", slotNumber);
console.log(" Typ:", selectedSlotType, "→", type);
console.log(" Modus:", selectedMode);
console.log(" Von:", vonDatum);
console.log(" Bis:", bisDatum);
console.log(" URL:", apiUrl);
console.log(" Daten:", jsonData);
//-----------------------
if (Array.isArray(jsonData) && jsonData.length > 0) {
dispatch(setLoopMeasurementCurveChartData(jsonData));
dispatch(setIsoMeasurementCurveChartData(jsonData));
dispatch(setChartOpen(true));
} else {
alert("⚠️ Keine Messdaten im gewählten Zeitraum gefunden.");
dispatch(setLoopMeasurementCurveChartData([]));
dispatch(setIsoMeasurementCurveChartData([]));
dispatch(setChartOpen(false));
}
} catch (err) {
@@ -193,7 +171,9 @@ const LoopChartActionBar: React.FC = () => {
return (
<div className="flex justify-between items-center p-2 bg-gray-100 rounded-lg space-x-2">
<div className="flex items-center">
<label className="text-sm font-semibold"> {slotNumber ?? "-"}</label>
<label className="text-sm font-semibold">
{slotNumber !== null ? slotNumber + 1 : "-"}
</label>
</div>
<div className="flex items-center space-x-2">
@@ -257,67 +237,10 @@ const LoopChartActionBar: React.FC = () => {
</div>
</Listbox>
<Listbox
value={selectedSlotType}
onChange={(value) => {
dispatch(setSelectedSlotType(value));
dispatch(
setChartTitle(
value === "isolationswiderstand"
? "Isolationsmessung"
: "Schleifenmessung"
)
);
}}
>
<div className="relative w-56">
<Listbox.Button className="w-full border px-3 py-1 rounded text-left bg-white flex justify-between items-center text-sm">
<span>
{
{
isolationswiderstand: "Isolationswiderstand",
schleifenwiderstand: "Schleifenwiderstand",
}[selectedSlotType]
}
</span>
<svg
className="w-5 h-5 text-gray-400"
viewBox="0 0 20 20"
fill="currentColor"
>
<path
fillRule="evenodd"
d="M5.23 7.21a.75.75 0 011.06.02L10 10.585l3.71-3.355a.75.75 0 111.02 1.1l-4.25 3.85a.75.75 0 01-1.02 0l-4.25-3.85a.75.75 0 01.02-1.06z"
clipRule="evenodd"
/>
</svg>
</Listbox.Button>
<Listbox.Options className="absolute z-50 mt-1 w-full border rounded bg-white shadow max-h-60 overflow-auto text-sm">
{["isolationswiderstand", "schleifenwiderstand"].map((type) => (
<Listbox.Option
key={type}
value={type}
className={({ selected, active }) =>
`px-4 py-1 cursor-pointer ${
selected
? "bg-littwin-blue text-white"
: active
? "bg-gray-200"
: ""
}`
}
>
{
{
isolationswiderstand: "Isolationswiderstand",
schleifenwiderstand: "Schleifenwiderstand",
}[type]
}
</Listbox.Option>
))}
</Listbox.Options>
</div>
</Listbox>
{/* Label für Isolationswiderstand - kein Dropdown mehr nötig */}
<div className="px-3 py-1 bg-gray-50 border rounded text-sm text-gray-700">
Isolationswiderstand
</div>
<button
onClick={handleFetchData}
@@ -337,4 +260,4 @@ const LoopChartActionBar: React.FC = () => {
);
};
export default LoopChartActionBar;
export default IsoChartActionBar;

View File

@@ -0,0 +1,174 @@
"use client"; // IsoChartView.tsx
import React, { useEffect } from "react";
import ReactModal from "react-modal";
import IsoMeasurementChart from "./IsoMeasurementChart";
import IsoChartActionBar from "./IsoChartActionBar";
import { useSelector, useDispatch } from "react-redux";
import { AppDispatch } from "@/redux/store";
import { RootState } from "@/redux/store";
import {
setChartOpen,
setFullScreen,
setSlotNumber,
} from "@/redux/slices/kabelueberwachungChartSlice";
import { resetBrushRange } from "@/redux/slices/brushSlice";
import {
setVonDatum,
setBisDatum,
setSelectedMode,
setSelectedSlotType,
} from "@/redux/slices/kabelueberwachungChartSlice";
interface IsoChartViewProps {
isOpen: boolean;
onClose: () => void;
slotIndex: number;
}
const IsoChartView: React.FC<IsoChartViewProps> = ({
isOpen,
onClose,
slotIndex,
}) => {
const dispatch = useDispatch<AppDispatch>();
const isFullScreen = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice.isFullScreen
);
// **Modal schließen + Redux-Status zurücksetzen**
const handleClose = () => {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
const toISO = (date: Date) => date.toLocaleDateString("sv-SE");
// Reset Datum
dispatch(setVonDatum(toISO(thirtyDaysAgo)));
dispatch(setBisDatum(toISO(today)));
// Reset Dropdowns
dispatch(setSelectedMode("DIA1"));
dispatch(setSelectedSlotType("isolationswiderstand"));
// Sonstiges Reset
dispatch(setChartOpen(false));
dispatch(setFullScreen(false));
dispatch(resetBrushRange());
onClose();
};
// **Vollbildmodus umschalten**
const toggleFullScreen = () => {
dispatch(setFullScreen(!isFullScreen));
};
// Modal öffnen - ISO spezifische Einstellungen
useEffect(() => {
if (isOpen) {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
const toISO = (date: Date) => date.toLocaleDateString("sv-SE");
// Set slot number first
dispatch(setSlotNumber(slotIndex));
// Set dates
dispatch(setVonDatum(toISO(thirtyDaysAgo)));
dispatch(setBisDatum(toISO(today)));
// Set ISO specific settings
dispatch(setSelectedSlotType("isolationswiderstand"));
}
}, [isOpen, slotIndex, dispatch]);
return (
<ReactModal
isOpen={isOpen}
onRequestClose={handleClose}
ariaHideApp={false}
style={{
overlay: { backgroundColor: "rgba(0, 0, 0, 0.5)" },
content: {
top: "50%",
left: "50%",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
width: isFullScreen ? "90vw" : "70rem",
height: isFullScreen ? "90vh" : "35rem",
padding: "1rem",
transition: "all 0.3s ease-in-out",
display: "flex",
flexDirection: "column",
},
}}
>
{/* Action-Buttons */}
<div
style={{
position: "absolute",
top: "0.625rem",
right: "0.625rem",
display: "flex",
gap: "0.75rem",
}}
>
{/* Fullscreen-Button */}
<button
onClick={toggleFullScreen}
style={{
background: "transparent",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
}}
>
<i
className={
isFullScreen ? "bi bi-fullscreen-exit" : "bi bi-arrows-fullscreen"
}
></i>
</button>
{/* Schließen-Button */}
<button
onClick={handleClose}
style={{
background: "transparent",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
}}
>
<i className="bi bi-x-circle-fill"></i>
</button>
</div>
{/* Chart-Container */}
<div
style={{
flex: 1,
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<h3 className="text-lg font-semibold">Isolationswiderstand</h3>
<IsoChartActionBar />
<div style={{ flex: 1, height: "90%" }}>
<IsoMeasurementChart />
</div>
</div>
</ReactModal>
);
};
export default IsoChartView;

View File

@@ -2,7 +2,7 @@
import React from "react";
interface CustomTooltipProps {
interface IsoCustomTooltipProps {
active?: boolean;
payload?: Array<{
dataKey: string;
@@ -16,7 +16,7 @@ interface CustomTooltipProps {
unit?: string;
}
const CustomTooltip: React.FC<CustomTooltipProps> = ({
const IsoCustomTooltip: React.FC<IsoCustomTooltipProps> = ({
active,
payload,
label,
@@ -67,4 +67,4 @@ const CustomTooltip: React.FC<CustomTooltipProps> = ({
return null;
};
export default CustomTooltip;
export default IsoCustomTooltip;

View File

@@ -1,4 +1,4 @@
"use client"; // /components/main/kabelueberwachung/kue705FO/Charts/LoopMeasurementChart/LoopMeasurementChart.tsx
"use client"; // /components/main/kabelueberwachung/kue705FO/Charts/IsoMeasurementChart/IsoMeasurementChart.tsx
import React, { useEffect, useRef } from "react";
import { useSelector } from "react-redux";
@@ -34,7 +34,7 @@ ChartJS.register(
import { getColor } from "@/utils/colors";
import { PulseLoader } from "react-spinners";
type LoopMeasurementEntry = {
type IsoMeasurementEntry = {
t: string;
i: number;
m: number;
@@ -42,20 +42,20 @@ type LoopMeasurementEntry = {
a: number;
};
const usePreviousData = (data: LoopMeasurementEntry[]) => {
const ref = useRef<LoopMeasurementEntry[]>([]);
const usePreviousData = (data: IsoMeasurementEntry[]) => {
const ref = useRef<IsoMeasurementEntry[]>([]);
useEffect(() => {
ref.current = data;
}, [data]);
return ref.current;
};
const LoopMeasurementChart = () => {
const IsoMeasurementChart = () => {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const chartInstance = useRef<ChartJS | null>(null);
const {
loopMeasurementCurveChartData,
isoMeasurementCurveChartData,
selectedMode,
unit,
isFullScreen,
@@ -64,12 +64,12 @@ const LoopMeasurementChart = () => {
bisDatum,
} = useSelector((state: RootState) => state.kabelueberwachungChartSlice);
const previousData = usePreviousData(loopMeasurementCurveChartData);
const previousData = usePreviousData(isoMeasurementCurveChartData);
// Vergleichsfunktion
const isEqual = (
a: LoopMeasurementEntry[],
b: LoopMeasurementEntry[]
a: IsoMeasurementEntry[],
b: IsoMeasurementEntry[]
): boolean => {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
@@ -102,7 +102,7 @@ const LoopMeasurementChart = () => {
const ctx = canvasRef.current.getContext("2d");
if (!ctx) return;
if (isEqual(loopMeasurementCurveChartData, previousData)) {
if (isEqual(isoMeasurementCurveChartData, previousData)) {
return; // keine echte Datenänderung → nicht neu zeichnen
}
@@ -111,13 +111,13 @@ const LoopMeasurementChart = () => {
}
const chartData = {
labels: loopMeasurementCurveChartData
labels: isoMeasurementCurveChartData
.map((entry) => new Date(entry.t))
.reverse(),
datasets: [
{
label: "Messwert Minimum",
data: loopMeasurementCurveChartData.map((e) => e.i).reverse(),
data: isoMeasurementCurveChartData.map((e) => e.i).reverse(),
borderColor: "lightgrey",
backgroundColor: "rgba(211,211,211,0.5)",
borderWidth: 2,
@@ -128,7 +128,7 @@ const LoopMeasurementChart = () => {
},
{
label: "Messwert Maximum",
data: loopMeasurementCurveChartData.map((e) => e.a).reverse(),
data: isoMeasurementCurveChartData.map((e) => e.a).reverse(),
borderColor: "lightgrey",
backgroundColor: "rgba(211,211,211,0.5)",
borderWidth: 2,
@@ -140,7 +140,7 @@ const LoopMeasurementChart = () => {
selectedMode === "DIA0"
? {
label: "Messwert",
data: loopMeasurementCurveChartData.map((e) => e.m).reverse(),
data: isoMeasurementCurveChartData.map((e) => e.m).reverse(),
borderColor: getColor("littwin-blue"),
backgroundColor: "rgba(59,130,246,0.5)",
borderWidth: 3,
@@ -151,7 +151,7 @@ const LoopMeasurementChart = () => {
}
: {
label: "Messwert Durchschnitt",
data: loopMeasurementCurveChartData.map((e) => e.g).reverse(),
data: isoMeasurementCurveChartData.map((e) => e.g).reverse(),
borderColor: getColor("littwin-blue"),
backgroundColor: "rgba(59,130,246,0.5)",
borderWidth: 3,
@@ -210,7 +210,7 @@ const LoopMeasurementChart = () => {
});
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [loopMeasurementCurveChartData, selectedMode, vonDatum, bisDatum]);
}, [isoMeasurementCurveChartData, selectedMode, vonDatum, bisDatum]);
return (
<div style={{ width: "100%", height: isFullScreen ? "90%" : "400px" }}>
@@ -231,4 +231,4 @@ const LoopMeasurementChart = () => {
);
};
export default LoopMeasurementChart;
export default IsoMeasurementChart;

View File

@@ -1,21 +1,20 @@
"use client"; // /components/modules/kue705FO/charts/ChartSwitcher.tsx
"use client"; // LoopChartView.tsx
import React, { useEffect } from "react";
import ReactModal from "react-modal";
import LoopChartActionBar from "./LoopMeasurementChart/LoopChartActionBar";
import LoopMeasurementChart from "./LoopMeasurementChart/LoopMeasurementChart";
import IsoMeasurementChart from "./IsoMeasurementChart/IsoMeasurementChart";
import TDRChart from "./TDRChart/TDRChart";
import LoopMeasurementChart from "./LoopMeasurementChart";
import LoopChartActionBar from "./LoopChartActionBar";
import { useSelector, useDispatch } from "react-redux";
import { AppDispatch } from "@/redux/store";
import { RootState } from "@/redux/store";
import {
setChartOpen,
setFullScreen,
setSlotNumber,
} from "@/redux/slices/kabelueberwachungChartSlice";
import { resetBrushRange } from "@/redux/slices/brushSlice";
import { useLoopChartLoader } from "./LoopMeasurementChart/LoopChartActionBar";
import { useLoopChartLoader } from "./LoopChartActionBar";
import {
setVonDatum,
@@ -24,26 +23,34 @@ import {
setSelectedSlotType,
} from "@/redux/slices/kabelueberwachungChartSlice";
interface ChartSwitcherProps {
interface LoopChartViewProps {
isOpen: boolean;
onClose: () => void;
slotIndex: number;
}
const ChartSwitcher: React.FC<ChartSwitcherProps> = ({ isOpen, onClose }) => {
const LoopChartView: React.FC<LoopChartViewProps> = ({
isOpen,
onClose,
slotIndex,
}) => {
const dispatch = useDispatch<AppDispatch>();
const chartTitle = useSelector(
(state: RootState) => state.loopChartType.chartTitle
);
// **Redux-States für aktive Messkurve (TDR oder Schleife)**
const activeMode = useSelector(
(state: RootState) => state.kueChartModeSlice.activeMode
);
const isFullScreen = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice.isFullScreen
);
// useLoopChartLoader hook
const loadLoopChartData = useLoopChartLoader();
// Slot number from Redux
const slotNumber = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice.slotNumber
);
// **Modal schließen + Redux-Status zurücksetzen**
const handleClose = () => {
const today = new Date();
@@ -73,50 +80,33 @@ const ChartSwitcher: React.FC<ChartSwitcherProps> = ({ isOpen, onClose }) => {
dispatch(setFullScreen(!isFullScreen));
};
// **Slot und Messkurve setzen**
// const setChartType = (chartType: "TDR" | "Schleife") => {
// dispatch(setSelectedSlot(slotIndex));
// dispatch(setSelectedChartType(chartType));
// };
// useLoopChartLoader hook
const loadLoopChartData = useLoopChartLoader();
// Slot number from Redux
const slotNumber = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice.slotNumber
);
// immer beim Öffnen das Modal die letzten 30 Tage anzeigen und SlotType setzen
// Modal öffnen - RSL spezifische Einstellungen
useEffect(() => {
if (isOpen) {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
const toISO = (date: Date) => date.toLocaleDateString("sv-SE"); // YYYY-MM-DD
const toISO = (date: Date) => date.toLocaleDateString("sv-SE");
// Set slot number first
dispatch(setSlotNumber(slotIndex));
// Set dates
dispatch(setVonDatum(toISO(thirtyDaysAgo)));
dispatch(setBisDatum(toISO(today)));
// Set SlotType based on activeMode
if (activeMode === "ISO") {
dispatch(setSelectedSlotType("isolationswiderstand"));
} else if (activeMode === "Schleife") {
dispatch(setSelectedSlotType("schleifenwiderstand"));
}
// Set RSL specific settings
dispatch(setSelectedSlotType("schleifenwiderstand"));
// Load data for Schleife mode
if (activeMode === "Schleife" && slotNumber !== null) {
// Warten, bis Redux gesetzt ist → dann Daten laden
setTimeout(() => {
loadLoopChartData.loadLoopChartData();
}, 10); // kleiner Delay, damit Redux-State sicher aktualisiert ist
}
// Load data for Schleife mode after a small delay to ensure Redux state is updated
setTimeout(() => {
loadLoopChartData.loadLoopChartData();
}, 10);
}
//ESLint ignore
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isOpen, activeMode, slotNumber, dispatch]);
}, [isOpen, slotIndex, dispatch]);
return (
<ReactModal
@@ -190,31 +180,14 @@ const ChartSwitcher: React.FC<ChartSwitcherProps> = ({ isOpen, onClose }) => {
height: "100%",
}}
>
{activeMode === "Schleife" ? (
<>
<h3 className="text-lg font-semibold">{chartTitle}</h3>
<LoopChartActionBar />
<div style={{ flex: 1, height: "90%" }}>
<LoopMeasurementChart />
</div>
</>
) : activeMode === "ISO" ? (
<>
<h3 className="text-lg font-semibold">Isolationswiderstand</h3>
<LoopChartActionBar />
<div style={{ flex: 1, height: "90%" }}>
<IsoMeasurementChart />
</div>
</>
) : (
<>
<h3 className="text-lg font-semibold">TDR-Messung</h3>
<TDRChart isFullScreen={isFullScreen} />
</>
)}
<h3 className="text-lg font-semibold">{chartTitle}</h3>
<LoopChartActionBar />
<div style={{ flex: 1, height: "90%" }}>
<LoopMeasurementChart />
</div>
</div>
</ReactModal>
);
};
export default ChartSwitcher;
export default LoopChartView;

View File

@@ -0,0 +1,179 @@
"use client"; // TDRChartView.tsx
import React, { useEffect } from "react";
import ReactModal from "react-modal";
import TDRChart from "./TDRChart";
import { useSelector, useDispatch } from "react-redux";
import { AppDispatch } from "@/redux/store";
import { RootState } from "@/redux/store";
import {
setChartOpen,
setFullScreen,
setSlotNumber,
} from "@/redux/slices/kabelueberwachungChartSlice";
import { resetBrushRange } from "@/redux/slices/brushSlice";
import {
setVonDatum,
setBisDatum,
setSelectedMode,
setSelectedSlotType,
} from "@/redux/slices/kabelueberwachungChartSlice";
import {
setSelectedSlot,
setActiveMode,
} from "@/redux/slices/kueChartModeSlice";
interface TDRChartViewProps {
isOpen: boolean;
onClose: () => void;
slotIndex: number;
}
const TDRChartView: React.FC<TDRChartViewProps> = ({
isOpen,
onClose,
slotIndex,
}) => {
const dispatch = useDispatch<AppDispatch>();
const isFullScreen = useSelector(
(state: RootState) => state.kabelueberwachungChartSlice.isFullScreen
);
// **Modal öffnen - TDR spezifische Einstellungen**
useEffect(() => {
if (isOpen) {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
const toISO = (date: Date) => date.toLocaleDateString("sv-SE");
// Set TDR mode and slot
dispatch(setActiveMode("TDR"));
dispatch(setSelectedSlot(slotIndex));
// Also set slot number for general chart slice
dispatch(setSlotNumber(slotIndex));
// Set dates
dispatch(setVonDatum(toISO(thirtyDaysAgo)));
dispatch(setBisDatum(toISO(today)));
// TDR specific settings (if needed)
dispatch(setSelectedSlotType("isolationswiderstand"));
}
}, [isOpen, slotIndex, dispatch]);
// **Modal schließen + Redux-Status zurücksetzen**
const handleClose = () => {
const today = new Date();
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(today.getDate() - 30);
const toISO = (date: Date) => date.toLocaleDateString("sv-SE");
// Reset Datum
dispatch(setVonDatum(toISO(thirtyDaysAgo)));
dispatch(setBisDatum(toISO(today)));
// Reset Dropdowns
dispatch(setSelectedMode("DIA1"));
dispatch(setSelectedSlotType("isolationswiderstand"));
// Sonstiges Reset
dispatch(setChartOpen(false));
dispatch(setFullScreen(false));
dispatch(resetBrushRange());
onClose();
};
// **Vollbildmodus umschalten**
const toggleFullScreen = () => {
dispatch(setFullScreen(!isFullScreen));
};
return (
<ReactModal
isOpen={isOpen}
onRequestClose={handleClose}
ariaHideApp={false}
style={{
overlay: { backgroundColor: "rgba(0, 0, 0, 0.5)" },
content: {
top: "50%",
left: "50%",
bottom: "auto",
marginRight: "-50%",
transform: "translate(-50%, -50%)",
width: isFullScreen ? "90vw" : "70rem",
height: isFullScreen ? "90vh" : "35rem",
padding: "1rem",
transition: "all 0.3s ease-in-out",
display: "flex",
flexDirection: "column",
},
}}
>
{/* Action-Buttons */}
<div
style={{
position: "absolute",
top: "0.625rem",
right: "0.625rem",
display: "flex",
gap: "0.75rem",
}}
>
{/* Fullscreen-Button */}
<button
onClick={toggleFullScreen}
style={{
background: "transparent",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
}}
>
<i
className={
isFullScreen ? "bi bi-fullscreen-exit" : "bi bi-arrows-fullscreen"
}
></i>
</button>
{/* Schließen-Button */}
<button
onClick={handleClose}
style={{
background: "transparent",
border: "none",
fontSize: "1.5rem",
cursor: "pointer",
}}
>
<i className="bi bi-x-circle-fill"></i>
</button>
</div>
{/* Chart-Container */}
<div
style={{
flex: 1,
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<h3 className="text-lg font-semibold">TDR-Messung</h3>
<TDRChart isFullScreen={isFullScreen} />
</div>
</ReactModal>
);
};
export default TDRChartView;

View File

@@ -4,9 +4,13 @@ import { useSelector } from "react-redux";
import KueModal from "./modals/SettingsModalWrapper";
import "bootstrap-icons/font/bootstrap-icons.css"; // Import Bootstrap Icons
import { Kue705FOProps } from "../../../../types/Kue705FOProps";
// Keep ChartSwitcher import
import ChartSwitcher from "./Charts/ChartSwitcher";
// Remove separate chart imports since we use ChartSwitcher
// Import the new specialized ChartView components
import IsoChartView from "./Charts/IsoMeasurementChart/IsoChartView";
import LoopChartView from "./Charts/LoopMeasurementChart/LoopChartView";
import TDRChartView from "./Charts/TDRChart/TDRChartView";
// Keep ChartSwitcher import for backwards compatibility if needed
// import ChartSwitcher from "./Charts/ChartSwitcher";
// Remove separate chart imports since we use ChartView components
// import IsoMeasurementChart from "./Charts/IsoMeasurementChart/IsoMeasurementChart";
// import LoopMeasurementChart from "./Charts/LoopMeasurementChart/LoopMeasurementChart";
//-------Redux Toolkit--------
@@ -26,10 +30,10 @@ import type { Chart } from "chart.js";
// Keep needed imports
import handleOpenModal from "./handlers/handleOpenModal";
import handleCloseModal from "./handlers/handleCloseModal";
// Restore chart modal handlers
import handleOpenChartModal from "./handlers/handleOpenChartModal";
import handleCloseChartModal from "./handlers/handleCloseChartModal";
import handleRefreshClick from "./handlers/handleRefreshClick";
// Remove unused chart modal handlers since we use direct ChartView components
// import handleOpenChartModal from "./handlers/handleOpenChartModal";
// import handleCloseChartModal from "./handlers/handleCloseChartModal";
// import handleRefreshClick from "./handlers/handleRefreshClick";
const Kue705FO: React.FC<Kue705FOProps> = ({
isolationswert,
@@ -63,11 +67,12 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
const [loading, setLoading] = useState(false);
const [showModal, setShowModal] = useState(false);
// Keep original showChartModal
const [showChartModal, setShowChartModal] = useState(false);
// Remove separate modals since we use ChartSwitcher
// const [showIsoModal, setShowIsoModal] = useState(false);
// const [showRslModal, setShowRslModal] = useState(false);
// Separate modal states for each ChartView
const [showIsoModal, setShowIsoModal] = useState(false);
const [showRslModal, setShowRslModal] = useState(false);
const [showTdrModal, setShowTdrModal] = useState(false);
// Keep original showChartModal for backwards compatibility if needed
// const [showChartModal, setShowChartModal] = useState(false);
// Removed unused loopMeasurementCurveChartData state
//------- Redux-Variablen abrufen--------------------------------
@@ -107,7 +112,7 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
const openModal = () => handleOpenModal(setShowModal);
const closeModal = () => handleCloseModal(setShowModal);
// Restore original chart modal handlers but set chart type automatically
// New ChartView handlers - direct modal opening
const openIsoModal = () => {
setActiveButton("ISO");
// Set Redux state for ISO type
@@ -119,8 +124,11 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
type: "kabelueberwachungChart/setSlotNumber",
payload: slotIndex,
});
// Open chart modal with ISO type
handleOpenChartModal(setShowChartModal, dispatch, slotIndex, "ISO");
setShowIsoModal(true);
};
const closeIsoModal = () => {
setShowIsoModal(false);
};
const openRslModal = () => {
@@ -135,15 +143,34 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
type: "kabelueberwachungChart/setSlotNumber",
payload: slotIndex,
});
handleOpenChartModal(setShowChartModal, dispatch, slotIndex, "Schleife");
setShowRslModal(true);
};
const refreshClick = () =>
handleRefreshClick(activeButton, slotIndex, setLoading);
const chartInstance = useRef<Chart | null>(null);
const closeRslModal = () => {
setShowRslModal(false);
};
const closeChartModal = () =>
handleCloseChartModal(setShowChartModal, chartInstance);
const openTdrModal = () => {
setActiveButton("TDR");
setloopTitleText("Entfernung [km]");
const latestTdrDistanceMeters =
Array.isArray(tdmChartData?.[slotIndex]) &&
tdmChartData[slotIndex].length > 0 &&
typeof tdmChartData[slotIndex][0].d === "number"
? tdmChartData[slotIndex][0].d
: 0;
const latestTdrDistance = Number(
(latestTdrDistanceMeters / 1000).toFixed(3)
);
setLoopDisplayValue(latestTdrDistance);
setShowTdrModal(true);
};
const closeTdrModal = () => {
setShowTdrModal(false);
};
//----------------------------------
//hooks einbinden
const kueVersion = useKueVersion(slotIndex, reduxKueVersion);
@@ -424,28 +451,7 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
{/* TDR and KVz Buttons */}
<div className="flex space-x-2 p-1">
<button
onClick={() => {
setActiveButton("TDR");
setloopTitleText("Entfernung [km]");
const latestTdrDistanceMeters =
Array.isArray(tdmChartData?.[slotIndex]) &&
tdmChartData[slotIndex].length > 0 &&
typeof tdmChartData[slotIndex][0].d === "number"
? tdmChartData[slotIndex][0].d
: 0;
const latestTdrDistance = Number(
(latestTdrDistanceMeters / 1000).toFixed(3)
);
setLoopDisplayValue(latestTdrDistance);
handleOpenChartModal(
setShowChartModal,
dispatch,
slotIndex,
"TDR"
);
}}
onClick={openTdrModal}
className=" bg-littwin-blue text-white text-[0.625rem] flex items-center justify-center p-2"
>
TDR
@@ -461,14 +467,26 @@ const Kue705FO: React.FC<Kue705FOProps> = ({
<div className="flex flex-col space-y-2 w-full"></div>
</div>
{/* Original Modal für Messkurve with ChartSwitcher */}
{showChartModal && (
<ChartSwitcher
isOpen={showChartModal}
onClose={closeChartModal}
slotIndex={slotIndex}
/>
)}
{/* ISO Chart Modal */}
<IsoChartView
isOpen={showIsoModal}
onClose={closeIsoModal}
slotIndex={slotIndex}
/>
{/* RSL Chart Modal */}
<LoopChartView
isOpen={showRslModal}
onClose={closeRslModal}
slotIndex={slotIndex}
/>
{/* TDR Chart Modal */}
<TDRChartView
isOpen={showTdrModal}
onClose={closeTdrModal}
slotIndex={slotIndex}
/>
</>
)}
{/* Offline-View */}

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "cpl-v4",
"version": "1.6.652",
"version": "1.6.653",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "cpl-v4",
"version": "1.6.652",
"version": "1.6.653",
"dependencies": {
"@fontsource/roboto": "^5.1.0",
"@headlessui/react": "^2.2.4",

View File

@@ -1,6 +1,6 @@
{
"name": "cpl-v4",
"version": "1.6.652",
"version": "1.6.653",
"private": true,
"scripts": {
"dev": "next dev",

View File

@@ -0,0 +1,48 @@
// /redux/slices/loopChartSlice.ts
import { createSlice } from "@reduxjs/toolkit";
import { getLoopChartDataThunk } from "../thunks/getLoopChartDataThunk";
interface ChartData {
[mode: string]: {
[type: number]: any;
};
}
interface LoopChartState {
data: ChartData;
loading: boolean;
error: string | null;
}
const initialState: LoopChartState = {
data: {},
loading: false,
error: null,
};
const isoChartSlice = createSlice({
name: "loopChartSlice",
initialState,
reducers: {},
extraReducers: (builder) => {
builder
.addCase(getLoopChartDataThunk.pending, (state) => {
state.loading = true;
state.error = null;
})
.addCase(getLoopChartDataThunk.fulfilled, (state, action) => {
state.loading = false;
const { mode, type } = action.meta.arg;
if (!state.data[mode]) {
state.data[mode] = {};
}
state.data[mode][type] = action.payload;
})
.addCase(getLoopChartDataThunk.rejected, (state, action) => {
state.loading = false;
state.error = action.payload as string;
});
},
});
export default isoChartSlice.reducer;

View File

@@ -0,0 +1,23 @@
// redux/slices/isoChartTypeSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
interface isoChartTypeState {
chartTitle: string;
}
const initialState: isoChartTypeState = {
chartTitle: "Isolationsmessung", // Standardwert
};
const isoChartTypeSlice = createSlice({
name: "isoChartType",
initialState,
reducers: {
setChartTitle: (state, action: PayloadAction<string>) => {
state.chartTitle = action.payload;
},
},
});
export const { setChartTitle } = isoChartTypeSlice.actions;
export default isoChartTypeSlice.reducer;

View File

@@ -11,6 +11,7 @@ interface TDRData {
// Definition des Interface für den gesamten Zustand der Kabelüberwachung
interface KabelueberwachungChartState {
loopMeasurementCurveChartData: any[];
isoMeasurementCurveChartData: any[];
vonDatum: string;
bisDatum: string;
selectedMode: "DIA0" | "DIA1" | "DIA2";
@@ -32,6 +33,7 @@ thirtyDaysAgo.setDate(today.getDate() - 30);
const initialState: KabelueberwachungChartState = {
isLoading: false,
loopMeasurementCurveChartData: [],
isoMeasurementCurveChartData: [],
vonDatum: thirtyDaysAgo.toISOString().split("T")[0], // 30 Tage zurück
bisDatum: today.toISOString().split("T")[0], // Heute
selectedMode: "DIA0",
@@ -54,6 +56,9 @@ const kabelueberwachungChartSlice = createSlice({
setLoopMeasurementCurveChartData: (state, action: PayloadAction<any[]>) => {
state.loopMeasurementCurveChartData = action.payload;
},
setIsoMeasurementCurveChartData: (state, action: PayloadAction<any[]>) => {
state.isoMeasurementCurveChartData = action.payload;
},
setVonDatum: (state, action: PayloadAction<string>) => {
state.vonDatum = action.payload;
},
@@ -95,6 +100,7 @@ const kabelueberwachungChartSlice = createSlice({
export const {
setSlotNumber,
setLoopMeasurementCurveChartData,
setIsoMeasurementCurveChartData,
setVonDatum,
setBisDatum,
setSelectedMode,