- Redux-Slice erweitert um `unit`-State (kOhm/MOhm) - `setSelectedSlotType` aktualisiert die Einheit basierend auf der Auswahl - Dropdown in `LoopChartActionBar.tsx` angepasst, um Einheit zu setzen - Y-Achse und Tooltip in `LoopMeasurementChart.tsx` zeigen dynamisch die Einheit aus Redux - Initialwert von `bisDatum` auf das heutige Datum gesetzt
244 lines
7.1 KiB
TypeScript
244 lines
7.1 KiB
TypeScript
"use client";
|
|
import React, { useCallback, useEffect, useMemo } from "react";
|
|
import { useSelector, useDispatch } from "react-redux";
|
|
import { RootState } from "../../../../../../redux/store";
|
|
import {
|
|
ComposedChart,
|
|
XAxis,
|
|
YAxis,
|
|
CartesianGrid,
|
|
Tooltip,
|
|
Legend,
|
|
ResponsiveContainer,
|
|
Line,
|
|
Brush,
|
|
} from "recharts";
|
|
import { setBrushRange } from "../../../../../../redux/slices/brushSlice";
|
|
|
|
const CustomTooltip = ({ active, payload, label, unit }: any) => {
|
|
if (active && payload && payload.length) {
|
|
const messwertMax = payload.find(
|
|
(p: any) => p.dataKey === "messwertMaximum"
|
|
);
|
|
const messwert = payload.find((p: any) => p.dataKey === "messwert");
|
|
const messwertMin = payload.find(
|
|
(p: any) => p.dataKey === "messwertMinimum"
|
|
);
|
|
const messwertDurchschnitt = payload.find(
|
|
(p: any) => p.dataKey === "messwertDurchschnitt"
|
|
);
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
background: "white",
|
|
padding: "8px",
|
|
border: "1px solid lightgrey",
|
|
borderRadius: "5px",
|
|
}}
|
|
>
|
|
<strong>{new Date(label).toLocaleString()}</strong>
|
|
{messwertMax && (
|
|
<div style={{ color: "grey" }}>
|
|
Messwert Maximum: {messwertMax.value.toFixed(2)} {unit}
|
|
</div>
|
|
)}
|
|
{messwert && (
|
|
<div style={{ color: "#00AEEF", fontWeight: "bold" }}>
|
|
Messwert: {messwert.value.toFixed(2)} {unit}
|
|
</div>
|
|
)}
|
|
{messwertDurchschnitt && (
|
|
<div
|
|
style={{ color: "#00AEEF" }}
|
|
>{`Messwert Durchschnitt: ${messwertDurchschnitt.value.toFixed(
|
|
2
|
|
)} ${unit}`}</div>
|
|
)}
|
|
{messwertMin && (
|
|
<div
|
|
style={{ color: "grey" }}
|
|
>{`Messwert Minimum: ${messwertMin.value.toFixed(2)} ${unit}`}</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
const LoopMeasurementChart = () => {
|
|
const dispatch = useDispatch();
|
|
const unit = useSelector(
|
|
(state: RootState) => state.kabelueberwachungChart.unit
|
|
);
|
|
|
|
const brushRange = useSelector((state: RootState) => state.brush);
|
|
const {
|
|
loopMeasurementCurveChartData,
|
|
selectedMode,
|
|
vonDatum,
|
|
bisDatum,
|
|
isFullScreen,
|
|
} = useSelector((state: RootState) => state.kabelueberwachungChart);
|
|
|
|
const formatierteDaten = useMemo(
|
|
() =>
|
|
loopMeasurementCurveChartData
|
|
.map((eintrag) => ({
|
|
zeit: new Date(eintrag.t).getTime(),
|
|
messwertMinimum: eintrag.i,
|
|
messwertMaximum: eintrag.a,
|
|
messwert: eintrag.m ?? null,
|
|
messwertDurchschnitt: ["DIA1", "DIA2"].includes(selectedMode)
|
|
? eintrag.g ?? null
|
|
: null,
|
|
}))
|
|
.reverse(),
|
|
[loopMeasurementCurveChartData, selectedMode]
|
|
);
|
|
|
|
// Initialisierung des Brush-Bereichs nur beim ersten Laden der Daten
|
|
useEffect(() => {
|
|
if (brushRange.endIndex === 0 && formatierteDaten.length) {
|
|
dispatch(
|
|
setBrushRange({
|
|
startIndex: 0,
|
|
endIndex: formatierteDaten.length - 1,
|
|
})
|
|
);
|
|
}
|
|
}, [formatierteDaten, brushRange.endIndex, dispatch]);
|
|
|
|
const handleBrushChange = useCallback(
|
|
({ startIndex, endIndex }) => {
|
|
dispatch(
|
|
setBrushRange({
|
|
startIndex,
|
|
endIndex,
|
|
startDate: new Date(formatierteDaten[startIndex].zeit)
|
|
.toISOString()
|
|
.split("T")[0],
|
|
endDate: new Date(formatierteDaten[endIndex].zeit)
|
|
.toISOString()
|
|
.split("T")[0],
|
|
})
|
|
);
|
|
},
|
|
[dispatch, formatierteDaten]
|
|
);
|
|
useEffect(() => {
|
|
if (formatierteDaten.length) {
|
|
const startIndex = formatierteDaten.findIndex(
|
|
(d) => new Date(d.zeit).toISOString().split("T")[0] === vonDatum
|
|
);
|
|
const endIndex = formatierteDaten.findIndex(
|
|
(d) => new Date(d.zeit).toISOString().split("T")[0] === bisDatum
|
|
);
|
|
|
|
if (startIndex !== -1 && endIndex !== -1) {
|
|
dispatch(
|
|
setBrushRange({
|
|
startIndex,
|
|
endIndex,
|
|
startDate: vonDatum,
|
|
endDate: bisDatum,
|
|
})
|
|
);
|
|
}
|
|
}
|
|
}, [vonDatum, bisDatum, formatierteDaten, dispatch]);
|
|
|
|
return (
|
|
<div style={{ width: "100%", height: isFullScreen ? "90%" : "400px" }}>
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<ComposedChart data={formatierteDaten}>
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
<XAxis
|
|
dataKey="zeit"
|
|
domain={["dataMin", "dataMax"]}
|
|
tickFormatter={(zeit) => new Date(zeit).toLocaleDateString()}
|
|
/>
|
|
<YAxis
|
|
label={{ value: unit, angle: -90, position: "insideLeft" }}
|
|
domain={["auto", "auto"]}
|
|
tickFormatter={(wert) => `${wert.toFixed(0)} `}
|
|
/>
|
|
<Tooltip content={<CustomTooltip unit={unit} />} />
|
|
<Legend
|
|
verticalAlign="top"
|
|
align="center"
|
|
content={({ payload }) => {
|
|
if (!payload) return null;
|
|
|
|
// Reihenfolge der Legende anpassen
|
|
const orderedPayload = [...payload].sort((a, b) => {
|
|
const order = [
|
|
"messwertMinimum",
|
|
"messwert",
|
|
"messwertDurchschnitt",
|
|
"messwertMaximum",
|
|
];
|
|
return order.indexOf(a.value) - order.indexOf(b.value);
|
|
});
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
width: "100%",
|
|
display: "flex",
|
|
justifyContent: "center",
|
|
}}
|
|
>
|
|
{orderedPayload.map((entry, index) => (
|
|
<span
|
|
key={index}
|
|
style={{ margin: "0 10px", color: entry.color }}
|
|
>
|
|
⬤ {entry.value}
|
|
</span>
|
|
))}
|
|
</div>
|
|
);
|
|
}}
|
|
/>
|
|
|
|
<Line
|
|
type="monotone"
|
|
dataKey="messwertMinimum"
|
|
stroke="lightgrey"
|
|
dot={false}
|
|
/>
|
|
<Line
|
|
type="monotone"
|
|
dataKey="messwertMaximum"
|
|
stroke="lightgrey"
|
|
dot={false}
|
|
/>
|
|
{["DIA1", "DIA2"].includes(selectedMode) && (
|
|
<Line
|
|
type="monotone"
|
|
dataKey="messwertDurchschnitt"
|
|
stroke="#00AEEF"
|
|
dot
|
|
/>
|
|
)}
|
|
{selectedMode === "DIA0" && (
|
|
<Line type="monotone" dataKey="messwert" stroke="#00AEEF" dot />
|
|
)}
|
|
<Brush
|
|
dataKey="zeit"
|
|
height={30}
|
|
stroke="#8884d8"
|
|
onChange={handleBrushChange}
|
|
startIndex={brushRange.startIndex}
|
|
endIndex={brushRange.endIndex || formatierteDaten.length - 1}
|
|
tickFormatter={(zeit) => new Date(zeit).toLocaleDateString()} // Datum statt Zahl
|
|
/>
|
|
</ComposedChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default LoopMeasurementChart;
|