feat: TDR Meldungen DateRangePicker
This commit is contained in:
@@ -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.886
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.887
|
||||
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_USE_CGI=true
|
||||
# App-Versionsnummer
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.886
|
||||
NEXT_PUBLIC_APP_VERSION=1.6.887
|
||||
NEXT_PUBLIC_CPL_MODE=production
|
||||
@@ -1,3 +1,8 @@
|
||||
## [1.6.887] – 2025-09-10
|
||||
|
||||
- style: actionbar in RSL und ISO
|
||||
|
||||
---
|
||||
## [1.6.886] – 2025-09-10
|
||||
|
||||
- WIP: von bis Zeitraum in ISO und TDR, aber TDR ist WIP
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AppDispatch } from "../../../../../../redux/store";
|
||||
import { Chart, registerables } from "chart.js";
|
||||
import "chartjs-adapter-date-fns";
|
||||
import { getColor } from "../../../../../../utils/colors";
|
||||
import TDRChartActionBar from "./TDRChartActionBar";
|
||||
import { getReferenceCurveBySlotThunk } from "../../../../../../redux/thunks/getReferenceCurveBySlotThunk";
|
||||
|
||||
const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
|
||||
@@ -212,8 +211,7 @@ const TDRChart: React.FC<{ isFullScreen: boolean }> = ({ isFullScreen }) => {
|
||||
//--------------------------------------
|
||||
|
||||
return (
|
||||
<div style={{ width: "100%", height: isFullScreen ? "90%" : "28rem" }}>
|
||||
<TDRChartActionBar />
|
||||
<div style={{ width: "100%", height: isFullScreen ? "90%" : "28rem" }}>
|
||||
|
||||
{tdrChartData.length === 0 ? (
|
||||
<div className="flex items-center justify-center h-full text-gray-500 italic">
|
||||
|
||||
@@ -153,7 +153,8 @@ const TDRChartActionBar: React.FC = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="toolbar w-full flex items-center gap-3 flex-wrap">
|
||||
<div className="toolbar w-full justify-between flex-wrap">
|
||||
{/* KÜ number left, controls right, like IsoChartActionBar */}
|
||||
<div className="flex items-center gap-2 pr-4">
|
||||
<span className="font-semibold uppercase tracking-wide text-muted">
|
||||
KÜ
|
||||
@@ -162,141 +163,145 @@ const TDRChartActionBar: React.FC = () => {
|
||||
{selectedSlot !== null ? selectedSlot + 1 : "-"}
|
||||
</span>
|
||||
</div>
|
||||
{/* Date range only in Meldungen (requirement) */}
|
||||
{isMeldungen && <DateRangePicker />}
|
||||
{isMeldungen ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleFetchMessages}
|
||||
className="btn-primary h-8 font-medium px-4"
|
||||
disabled={selectedSlot === null}
|
||||
>
|
||||
Anzeigen
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
{selectedId !== null && (
|
||||
<div className="flex items-center gap-3 flex-1 justify-end">
|
||||
{isMeldungen ? (
|
||||
<>
|
||||
<DateRangePicker />
|
||||
<button
|
||||
onClick={handleSetReference}
|
||||
type="button"
|
||||
className="btn-primary h-8 px-3 font-medium"
|
||||
onClick={handleFetchMessages}
|
||||
className="btn-primary h-8 font-medium px-4"
|
||||
disabled={selectedSlot === null}
|
||||
>
|
||||
TDR-Kurve als Referenz speichern
|
||||
Anzeigen
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleStartTDR}
|
||||
type="button"
|
||||
disabled={selectedSlot === null || tdrRunning}
|
||||
className={`btn-primary h-8 px-4 whitespace-nowrap ${
|
||||
tdrRunning ? "opacity-90" : ""
|
||||
}`}
|
||||
>
|
||||
{tdrRunning
|
||||
? `TDR läuft... (${Math.min(
|
||||
100,
|
||||
Math.round((tdrProgress / TDR_TOTAL_DURATION) * 100)
|
||||
)}%)`
|
||||
: "TDR-Messung starten"}
|
||||
</button>
|
||||
<div className="ml-auto flex-1 min-w-[14rem] max-w-[30rem]">
|
||||
<Listbox
|
||||
value={selectedId}
|
||||
onChange={(id) => {
|
||||
setSelectedId(id);
|
||||
if (id !== null) dispatch(getTDRChartDataByIdThunk(id));
|
||||
}}
|
||||
disabled={idsForSlot.length === 0}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{selectedId !== null && (
|
||||
<button
|
||||
onClick={handleSetReference}
|
||||
type="button"
|
||||
className="btn-primary h-8 px-3 font-medium"
|
||||
disabled={selectedSlot === null}
|
||||
>
|
||||
TDR-Kurve als Referenz speichern
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={handleStartTDR}
|
||||
type="button"
|
||||
disabled={selectedSlot === null || tdrRunning}
|
||||
className={`btn-primary h-8 px-4 whitespace-nowrap ${
|
||||
tdrRunning ? "opacity-90" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="relative w-full">
|
||||
<Listbox.Button className="dropdown-surface w-full flex items-center justify-between h-8 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<span className="dropdown-text-fix whitespace-nowrap overflow-hidden text-ellipsis pr-2">
|
||||
{selectedId
|
||||
? (() => {
|
||||
const selected = idsForSlot.find(
|
||||
(e) => e.id === selectedId
|
||||
);
|
||||
return selected
|
||||
? `${new Date(selected.t).toLocaleString(
|
||||
"de-DE",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}
|
||||
)} – Fehlerstelle: ${selected.d} m`
|
||||
: "Wähle Messung";
|
||||
})()
|
||||
: "Wähle Messung"}
|
||||
</span>
|
||||
<i className="bi bi-chevron-down text-sm opacity-70" />
|
||||
</Listbox.Button>
|
||||
<Listbox.Options className="dropdown-options absolute z-50 mt-1 w-full max-h-72 overflow-auto text-sm bg-[var(--color-surface)] border border-base rounded-md shadow-lg p-1">
|
||||
{idsForSlot.map((entry) => {
|
||||
const dateLabel = new Date(entry.t).toLocaleString(
|
||||
"de-DE",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}
|
||||
);
|
||||
const fullText = `${dateLabel} – Fehlerstelle: ${entry.d} m`;
|
||||
return (
|
||||
<Listbox.Option
|
||||
key={entry.id}
|
||||
value={entry.id}
|
||||
title={fullText}
|
||||
className={({ selected, active }) => {
|
||||
const base =
|
||||
"px-3 h-8 cursor-pointer rounded-sm m-0.5 flex items-center justify-start transition-colors text-[13px]";
|
||||
if (selected)
|
||||
return `${base} dropdown-option-active font-medium`;
|
||||
if (active) return `${base} dropdown-option-hover`;
|
||||
return `${base}`;
|
||||
}}
|
||||
>
|
||||
<span className="truncate w-full">{fullText}</span>
|
||||
</Listbox.Option>
|
||||
);
|
||||
})}
|
||||
</Listbox.Options>
|
||||
</div>
|
||||
</Listbox>
|
||||
{tdrRunning
|
||||
? `TDR läuft... (${Math.min(
|
||||
100,
|
||||
Math.round((tdrProgress / TDR_TOTAL_DURATION) * 100)
|
||||
)}%)`
|
||||
: "TDR-Messung starten"}
|
||||
</button>
|
||||
<div className="ml-auto flex-1 min-w-[14rem] max-w-[30rem]">
|
||||
<Listbox
|
||||
value={selectedId}
|
||||
onChange={(id) => {
|
||||
setSelectedId(id);
|
||||
if (id !== null) dispatch(getTDRChartDataByIdThunk(id));
|
||||
}}
|
||||
disabled={idsForSlot.length === 0}
|
||||
>
|
||||
<div className="relative w-full">
|
||||
<Listbox.Button className="dropdown-surface w-full flex items-center justify-between h-8 disabled:opacity-50 disabled:cursor-not-allowed">
|
||||
<span className="dropdown-text-fix whitespace-nowrap overflow-hidden text-ellipsis pr-2">
|
||||
{selectedId
|
||||
? (() => {
|
||||
const selected = idsForSlot.find(
|
||||
(e) => e.id === selectedId
|
||||
);
|
||||
return selected
|
||||
? `${new Date(selected.t).toLocaleString(
|
||||
"de-DE",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}
|
||||
)} – Fehlerstelle: ${selected.d} m`
|
||||
: "Wähle Messung";
|
||||
})()
|
||||
: "Wähle Messung"}
|
||||
</span>
|
||||
<i className="bi bi-chevron-down text-sm opacity-70" />
|
||||
</Listbox.Button>
|
||||
<Listbox.Options className="dropdown-options absolute z-50 mt-1 w-full max-h-72 overflow-auto text-sm bg-[var(--color-surface)] border border-base rounded-md shadow-lg p-1">
|
||||
{idsForSlot.map((entry) => {
|
||||
const dateLabel = new Date(entry.t).toLocaleString(
|
||||
"de-DE",
|
||||
{
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
second: "2-digit",
|
||||
}
|
||||
);
|
||||
const fullText = `${dateLabel} – Fehlerstelle: ${entry.d} m`;
|
||||
return (
|
||||
<Listbox.Option
|
||||
key={entry.id}
|
||||
value={entry.id}
|
||||
title={fullText}
|
||||
className={({ selected, active }) => {
|
||||
const base =
|
||||
"px-3 h-8 cursor-pointer rounded-sm m-0.5 flex items-center justify-start transition-colors text-[13px]";
|
||||
if (selected)
|
||||
return `${base} dropdown-option-active font-medium`;
|
||||
if (active) return `${base} dropdown-option-hover`;
|
||||
return `${base}`;
|
||||
}}
|
||||
>
|
||||
<span className="truncate w-full">{fullText}</span>
|
||||
</Listbox.Option>
|
||||
);
|
||||
})}
|
||||
</Listbox.Options>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{/* Progress Overlay */}
|
||||
{tdrRunning && (
|
||||
<div className="fixed inset-0 z-[1000] flex flex-col items-center justify-center bg-[rgba(0,0,0,0.55)] backdrop-blur-sm">
|
||||
<div className="mb-4 text-center space-y-1">
|
||||
<p className="text-lg font-semibold text-white">
|
||||
TDR Messung läuft... kann bis zu zwei Minuten dauern
|
||||
</p>
|
||||
<p className="text-sm text-white/80">
|
||||
Bitte warten…{" "}
|
||||
{Math.min(
|
||||
100,
|
||||
Math.round((tdrProgress / TDR_TOTAL_DURATION) * 100)
|
||||
)}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
<div className="w-2/3 max-w-xl h-3 bg-white/20 rounded overflow-hidden shadow-inner">
|
||||
<div
|
||||
className="h-full bg-accent transition-all ease-linear"
|
||||
style={{ width: `${(tdrProgress / TDR_TOTAL_DURATION) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{tdrRunning && (
|
||||
<div className="fixed inset-0 z-[1000] flex flex-col items-center justify-center bg-[rgba(0,0,0,0.55)] backdrop-blur-sm">
|
||||
<div className="mb-4 text-center space-y-1">
|
||||
<p className="text-lg font-semibold text-white">
|
||||
TDR Messung läuft... kann bis zu zwei Minuten dauern
|
||||
</p>
|
||||
<p className="text-sm text-white/80">
|
||||
Bitte warten…{" "}
|
||||
{Math.min(
|
||||
100,
|
||||
Math.round((tdrProgress / TDR_TOTAL_DURATION) * 100)
|
||||
)}
|
||||
%
|
||||
</p>
|
||||
</div>
|
||||
<div className="w-2/3 max-w-xl h-3 bg-white/20 rounded overflow-hidden shadow-inner">
|
||||
<div
|
||||
className="h-full bg-accent transition-all ease-linear"
|
||||
style={{ width: `${(tdrProgress / TDR_TOTAL_DURATION) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import React, { useEffect } from "react";
|
||||
import ReactModal from "react-modal";
|
||||
import TDRChart from "./TDRChart";
|
||||
import TDRChartActionBar from "./TDRChartActionBar";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { AppDispatch, RootState } from "@/redux/store";
|
||||
import {
|
||||
@@ -166,6 +167,8 @@ const TDRChartView: React.FC<TDRChartViewProps> = ({
|
||||
</div>
|
||||
</header>
|
||||
<div className="flex flex-col flex-1 p-3 gap-3">
|
||||
{/* Action Bar (wie bei ISO / Loop) */}
|
||||
<TDRChartActionBar />
|
||||
<div className="flex-1 relative">
|
||||
{chartTitle === "Messkurve" ? (
|
||||
<TDRChart isFullScreen={isFullScreen} />
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.886",
|
||||
"version": "1.6.887",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.886",
|
||||
"version": "1.6.887",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.13.0",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cpl-v4",
|
||||
"version": "1.6.886",
|
||||
"version": "1.6.887",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev -p 3000",
|
||||
|
||||
Reference in New Issue
Block a user