feat(analogInputsChart): zeige Minimum (i) und Maximum (a) als zusätzliche Linien im Chart

- Chart zeigt jetzt Messwert (m), Minimum (i, grün) und Maximum (a, rot) für ausgewählten Zeitraum
- Tooltip und Legende angepasst
- Typdefinitionen für Chart
This commit is contained in:
ISA
2025-07-21 10:21:45 +02:00
parent 528773128d
commit 23a3c173dd
7 changed files with 61 additions and 18 deletions

View File

@@ -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.605 NEXT_PUBLIC_APP_VERSION=1.6.607
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)

View File

@@ -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.605 NEXT_PUBLIC_APP_VERSION=1.6.607
NEXT_PUBLIC_CPL_MODE=production NEXT_PUBLIC_CPL_MODE=production

View File

@@ -1,3 +1,13 @@
## [1.6.607] 2025-07-21
- Nach Betriebsferien einmal sichern
---
## [1.6.606] 2025-07-21
- Nach Betriebsferien einmal sichern
---
## [1.6.605] 2025-07-21 ## [1.6.605] 2025-07-21
- Nach Betriebsferien einmal sichern - Nach Betriebsferien einmal sichern

View File

@@ -1,4 +1,5 @@
"use client"; "use client";
// components/main/analogInputs/AnalogInputsChart.tsx
import React, { useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { RootState, AppDispatch } from "@/redux/store"; import { RootState, AppDispatch } from "@/redux/store";
@@ -13,8 +14,8 @@ import {
Legend, Legend,
Filler, Filler,
TimeScale, TimeScale,
TooltipItem,
} from "chart.js"; } from "chart.js";
import type { ChartOptions } from "chart.js";
import "chartjs-adapter-date-fns"; import "chartjs-adapter-date-fns";
import { de } from "date-fns/locale"; import { de } from "date-fns/locale";
import { getAnalogInputsHistoryThunk } from "@/redux/thunks/getAnalogInputsHistoryThunk"; import { getAnalogInputsHistoryThunk } from "@/redux/thunks/getAnalogInputsHistoryThunk";
@@ -42,6 +43,8 @@ ChartJS.register(
type AnalogInputHistoryPoint = { type AnalogInputHistoryPoint = {
t: string; t: string;
m: number; m: number;
i?: number;
a?: number;
}; };
export default function AnalogInputsChart() { export default function AnalogInputsChart() {
@@ -56,7 +59,7 @@ export default function AnalogInputsChart() {
}, []); }, []);
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const chartRef = useRef<any>(null); const chartRef = useRef<ChartJS | null>(null);
const { zeitraum, vonDatum, bisDatum, data, autoLoad, selectedId } = const { zeitraum, vonDatum, bisDatum, data, autoLoad, selectedId } =
useSelector((state: RootState) => state.analogInputsHistory); useSelector((state: RootState) => state.analogInputsHistory);
@@ -88,7 +91,9 @@ export default function AnalogInputsChart() {
chart.options.scales.x.max = new Date(latestBisDatum).getTime(); chart.options.scales.x.max = new Date(latestBisDatum).getTime();
// Aktualisiere die Daten des Diagramms // Aktualisiere die Daten des Diagramms
const chartKey = selectedAnalogInput?.id ? String(selectedAnalogInput.id + 99) : null; const chartKey = selectedAnalogInput?.id
? String(selectedAnalogInput.id + 99)
: null;
const inputData = chartKey ? data[chartKey] ?? [] : []; const inputData = chartKey ? data[chartKey] ?? [] : [];
const filteredData = inputData.filter((point) => { const filteredData = inputData.filter((point) => {
const date = new Date(point.t); const date = new Date(point.t);
@@ -170,12 +175,9 @@ export default function AnalogInputsChart() {
? [ ? [
{ {
label: selectedAnalogInput?.label label: selectedAnalogInput?.label
? `Messwerteingang ${selectedAnalogInput.label}` ? `Messwert (m) ${selectedAnalogInput.label}`
: "Messwerte", : "Messwert (m)",
data: filteredData.map((point) => ({ data: filteredData.map((point) => ({ x: point.t, y: point.m })),
x: point.t,
y: point.m,
})),
fill: false, fill: false,
borderColor: getColor("littwin-blue"), borderColor: getColor("littwin-blue"),
backgroundColor: "rgba(59,130,246,0.3)", backgroundColor: "rgba(59,130,246,0.3)",
@@ -183,6 +185,30 @@ export default function AnalogInputsChart() {
pointRadius: 0, pointRadius: 0,
tension: 0.1, tension: 0.1,
}, },
{
label: "Minimum (i)",
data: filteredData
.filter((point) => typeof point.i === "number")
.map((point) => ({ x: point.t, y: point.i })),
fill: false,
borderColor: "#22c55e", // grün
borderWidth: 1,
pointRadius: 0,
borderDash: [4, 2],
tension: 0.1,
},
{
label: "Maximum (a)",
data: filteredData
.filter((point) => typeof point.a === "number")
.map((point) => ({ x: point.t, y: point.a })),
fill: false,
borderColor: "#ef4444", // rot
borderWidth: 1,
pointRadius: 0,
borderDash: [4, 2],
tension: 0.1,
},
] ]
: [], : [],
}; };
@@ -195,10 +221,11 @@ export default function AnalogInputsChart() {
mode: "index" as const, mode: "index" as const,
intersect: false, intersect: false,
callbacks: { callbacks: {
label: function (context: any) { label: function (context: TooltipItem<"line">) {
return `Messwert: ${context.parsed.y}`; const label = context.dataset.label || "";
return `${label}: ${context.parsed.y}`;
}, },
title: function (tooltipItems: any[]) { title: function (tooltipItems: TooltipItem<"line">[]) {
const date = tooltipItems[0].parsed.x; const date = tooltipItems[0].parsed.x;
return `Zeitpunkt: ${new Date(date).toLocaleString("de-DE")}`; return `Zeitpunkt: ${new Date(date).toLocaleString("de-DE")}`;
}, },
@@ -212,7 +239,11 @@ export default function AnalogInputsChart() {
}, },
zoom: { zoom: {
pan: { enabled: true, mode: "x" as const }, pan: { enabled: true, mode: "x" as const },
zoom: { wheel: { enabled: true }, pinch: { enabled: true }, mode: "x" as const }, zoom: {
wheel: { enabled: true },
pinch: { enabled: true },
mode: "x" as const,
},
}, },
}, },
scales: { scales: {

4
package-lock.json generated
View File

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

View File

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

View File

@@ -5,6 +5,8 @@ import { getAnalogInputsHistoryThunk } from "../../thunks/getAnalogInputsHistory
export type AnalogInputsHistoryEntry = { export type AnalogInputsHistoryEntry = {
t: string; t: string;
m: number; m: number;
i?: number;
a?: number;
}; };
export interface InputHistoryState { export interface InputHistoryState {