feat: TDR Meldungen DateRangePicker

This commit is contained in:
ISA
2025-09-10 08:13:37 +02:00
parent d6703c8870
commit 28612f9cd0
8 changed files with 146 additions and 135 deletions

View File

@@ -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">

View File

@@ -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">
@@ -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>
)}
</>
);
};

View File

@@ -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} />