feat(ui): Slot-Nummer nach links verschoben und Datumsauswahl horizontal ausgerichtet
- Slot-Nr.-Anzeige nach links im ActionBar verschoben. - Datum-Labels („Von“ & „Bis“) und Eingabefelder horizontal ausgerichtet. - Verbesserte UI/UX der Chart-Steuerungskomponenten.
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
setChartOpen,
|
||||
setFullScreen,
|
||||
} from "../../../../../redux/slices/kabelueberwachungChartSlice";
|
||||
import { resetBrushRange } from "../../../../../redux/slices/brushSlice";
|
||||
|
||||
interface ChartSwitcherProps {
|
||||
isOpen: boolean;
|
||||
@@ -30,6 +31,7 @@ const ChartSwitcher: React.FC<ChartSwitcherProps> = ({ isOpen, onClose }) => {
|
||||
const handleClose = () => {
|
||||
dispatch(setChartOpen(false));
|
||||
dispatch(setFullScreen(false));
|
||||
dispatch(resetBrushRange());
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
||||
@@ -8,9 +8,20 @@ import {
|
||||
} from "../../../../../redux/slices/kabelueberwachungChartSlice";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
|
||||
const DateRangePicker: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
// ✅ Props definieren
|
||||
interface DateRangePickerProps {
|
||||
setVonDatum: (date: Date) => void;
|
||||
setBisDatum: (date: Date) => void;
|
||||
minDate: string;
|
||||
maxDate: string;
|
||||
}
|
||||
|
||||
const DateRangePicker: React.FC<DateRangePickerProps> = ({
|
||||
setVonDatum,
|
||||
setBisDatum,
|
||||
minDate,
|
||||
maxDate,
|
||||
}) => {
|
||||
const reduxVonDatum = useSelector(
|
||||
(state: RootState) => state.kabelueberwachungChart.vonDatum
|
||||
);
|
||||
@@ -19,37 +30,40 @@ const DateRangePicker: React.FC = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex space-x-4">
|
||||
<div>
|
||||
<div className="flex space-x-4 items-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<label className="block text-sm font-semibold">Von</label>
|
||||
<DatePicker
|
||||
selected={reduxVonDatum ? new Date(reduxVonDatum) : new Date()}
|
||||
onChange={(date) => {
|
||||
if (date) {
|
||||
dispatch(setVonDatum(date.toISOString().split("T")[0]));
|
||||
setVonDatum(date);
|
||||
}
|
||||
}}
|
||||
selectsStart
|
||||
startDate={reduxVonDatum ? new Date(reduxVonDatum) : new Date()}
|
||||
endDate={reduxBisDatum ? new Date(reduxBisDatum) : new Date()}
|
||||
minDate={new Date(minDate)}
|
||||
maxDate={new Date(maxDate)}
|
||||
dateFormat="dd.MM.yyyy"
|
||||
className="border px-2 py-1 rounded"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<label className="block text-sm font-semibold">Bis</label>
|
||||
<DatePicker
|
||||
selected={reduxBisDatum ? new Date(reduxBisDatum) : new Date()}
|
||||
onChange={(date) => {
|
||||
if (date) {
|
||||
dispatch(setBisDatum(date.toISOString().split("T")[0]));
|
||||
setBisDatum(date);
|
||||
}
|
||||
}}
|
||||
selectsEnd
|
||||
startDate={reduxVonDatum ? new Date(reduxVonDatum) : new Date()}
|
||||
endDate={reduxBisDatum ? new Date(reduxBisDatum) : new Date()}
|
||||
minDate={reduxVonDatum ? new Date(reduxVonDatum) : new Date()}
|
||||
minDate={new Date(minDate)}
|
||||
maxDate={new Date(maxDate)}
|
||||
dateFormat="dd.MM.yyyy"
|
||||
className="border px-2 py-1 rounded"
|
||||
/>
|
||||
|
||||
@@ -23,22 +23,28 @@ const LoopChartActionBar: React.FC = () => {
|
||||
selectedSlotType,
|
||||
isChartOpen,
|
||||
slotNumber,
|
||||
loopMeasurementCurveChartData, // ⬅️ HIER FEHLTE DIESE ZEILE
|
||||
loopMeasurementCurveChartData,
|
||||
} = useSelector((state: RootState) => state.kabelueberwachungChart);
|
||||
|
||||
/**
|
||||
* API-URL-Erstellung für Entwicklung und Produktion
|
||||
*/
|
||||
const getApiUrl = (mode: "DIA0" | "DIA1" | "DIA2", type: number) => {
|
||||
const getApiUrl = (
|
||||
mode: "DIA0" | "DIA1" | "DIA2",
|
||||
type: number,
|
||||
slotNumber: number
|
||||
) => {
|
||||
if (!slotNumber) {
|
||||
console.error("⚠️ Slot-Nummer nicht gesetzt!");
|
||||
return "";
|
||||
}
|
||||
|
||||
// Dynamische Basis-URL abhängig von Umgebung
|
||||
const typeFolder =
|
||||
type === 3 ? "isolationswiderstand" : "schleifenwiderstand";
|
||||
|
||||
const baseUrl =
|
||||
process.env.NODE_ENV === "development"
|
||||
? `/CPLmockData/kuesChartData/${mode}_${type}.json`
|
||||
? `/CPLmockData/kuesChartData/slot${slotNumber}/${typeFolder}/${mode}.json`
|
||||
: `${window.location.origin}/CPL?seite.ACP&${mode}=${formatDate(
|
||||
vonDatum
|
||||
)};${formatDate(bisDatum)};${slotNumber};${type};`;
|
||||
@@ -52,18 +58,21 @@ const LoopChartActionBar: React.FC = () => {
|
||||
return `${dateParts[0]};${dateParts[1]};${dateParts[2]}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Funktion zum Laden der Messwerte
|
||||
*/
|
||||
/**
|
||||
* Funktion zum Laden der Messwerte
|
||||
*/
|
||||
const handleFetchData = async () => {
|
||||
const type = selectedSlotType === "schleifenwiderstand" ? 4 : 3;
|
||||
const apiUrl = getApiUrl(selectedMode, type);
|
||||
if (slotNumber === null) {
|
||||
console.error("⚠️ Slot-Nummer nicht gesetzt!");
|
||||
return;
|
||||
}
|
||||
const apiUrl = getApiUrl(selectedMode, type, slotNumber); // ✅ slotNumber ergänzt
|
||||
|
||||
if (!apiUrl) return;
|
||||
|
||||
try {
|
||||
console.log("📡 API-Request an:", apiUrl); // Debugging
|
||||
console.log("📡 API-Request an:", apiUrl);
|
||||
const response = await fetch(apiUrl, {
|
||||
method: "GET",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -75,7 +84,7 @@ const LoopChartActionBar: React.FC = () => {
|
||||
console.log("✅ Daten erfolgreich geladen:", jsonData);
|
||||
|
||||
if (Array.isArray(jsonData)) {
|
||||
dispatch(setLoopMeasurementCurveChartData([...jsonData])); // Erzwingt eine neue Referenz
|
||||
dispatch(setLoopMeasurementCurveChartData([...jsonData]));
|
||||
|
||||
// Falls das Chart zum ersten Mal geöffnet wird, setze vonDatum & bisDatum
|
||||
if (!isChartOpen && jsonData.length > 0) {
|
||||
@@ -83,22 +92,18 @@ const LoopChartActionBar: React.FC = () => {
|
||||
const lastDate = new Date(jsonData[0].t);
|
||||
dispatch(setVonDatum(firstDate.toISOString().split("T")[0]));
|
||||
dispatch(setBisDatum(lastDate.toISOString().split("T")[0]));
|
||||
dispatch(setChartOpen(true)); // Chart öffnen
|
||||
dispatch(setChartOpen(true));
|
||||
}
|
||||
} else {
|
||||
console.error("⚠️ Erwartetes Array, aber erhalten:", jsonData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Fehler beim Laden der Produktions-Daten:", error);
|
||||
console.error("❌ Fehler beim Laden der Daten:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const minDate = useMemo(() => {
|
||||
if (
|
||||
!loopMeasurementCurveChartData ||
|
||||
loopMeasurementCurveChartData.length === 0
|
||||
)
|
||||
return "";
|
||||
if (!loopMeasurementCurveChartData?.length) return "";
|
||||
return new Date(
|
||||
loopMeasurementCurveChartData[loopMeasurementCurveChartData.length - 1].t
|
||||
)
|
||||
@@ -107,77 +112,78 @@ const LoopChartActionBar: React.FC = () => {
|
||||
}, [loopMeasurementCurveChartData]);
|
||||
|
||||
const maxDate = useMemo(() => {
|
||||
if (
|
||||
!loopMeasurementCurveChartData ||
|
||||
loopMeasurementCurveChartData.length === 0
|
||||
)
|
||||
return "";
|
||||
if (!loopMeasurementCurveChartData?.length) return "";
|
||||
return new Date(loopMeasurementCurveChartData[0].t)
|
||||
.toISOString()
|
||||
.split("T")[0];
|
||||
}, [loopMeasurementCurveChartData]);
|
||||
|
||||
// **Automatische Datenaktualisierung bei Auswahländerung**
|
||||
// Automatische Datenaktualisierung bei Auswahländerung
|
||||
useEffect(() => {
|
||||
handleFetchData();
|
||||
}, [selectedMode, selectedSlotType]); // Wird ausgeführt, wenn sich ein Dropdown ändert
|
||||
// Wenn sich slotNumber, Zeitraum oder Auswahl ändert, neu laden:
|
||||
}, [selectedMode, selectedSlotType, slotNumber, vonDatum, bisDatum]);
|
||||
|
||||
return (
|
||||
<div className="flex justify-end items-center p-2 bg-gray-100 rounded-lg space-x-2">
|
||||
{/* Datumsauswahl */}
|
||||
<DateRangePicker
|
||||
setVonDatum={(date) => {
|
||||
const isoDate = date.toISOString().split("T")[0];
|
||||
<div className="flex justify-between items-center p-2 bg-gray-100 rounded-lg space-x-2">
|
||||
{/* Slot Nummer links positioniert */}
|
||||
<div className="flex items-center">
|
||||
<label className="text-sm font-semibold">
|
||||
Slot-Nr.: {slotNumber ?? "-"}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
if (isoDate < minDate || isoDate > maxDate) return;
|
||||
<div className="flex items-center space-x-2">
|
||||
<DateRangePicker
|
||||
setVonDatum={(date) => {
|
||||
const isoDate = date.toISOString().split("T")[0];
|
||||
|
||||
dispatch(setVonDatum(isoDate));
|
||||
if (isoDate < minDate || isoDate > maxDate) return;
|
||||
|
||||
if (isoDate > bisDatum) {
|
||||
dispatch(setBisDatum(isoDate)); // Sicherstellen, dass bisDatum >= vonDatum bleibt
|
||||
}
|
||||
}}
|
||||
setBisDatum={(date) => {
|
||||
const isoDate = date.toISOString().split("T")[0];
|
||||
dispatch(setVonDatum(isoDate));
|
||||
|
||||
if (isoDate < minDate || isoDate > maxDate) return;
|
||||
if (isoDate > bisDatum) dispatch(setBisDatum(isoDate));
|
||||
}}
|
||||
setBisDatum={(date) => {
|
||||
const isoDate = date.toISOString().split("T")[0];
|
||||
|
||||
if (isoDate >= vonDatum) {
|
||||
dispatch(setBisDatum(isoDate));
|
||||
}
|
||||
}}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
/>
|
||||
if (isoDate < minDate || isoDate > maxDate) return;
|
||||
|
||||
{/* Dropdown für DIA-Modus */}
|
||||
<select
|
||||
value={selectedMode}
|
||||
onChange={(e) =>
|
||||
dispatch(setSelectedMode(e.target.value as "DIA0" | "DIA1" | "DIA2"))
|
||||
}
|
||||
className="px-3 py-1 bg-white border rounded text-sm"
|
||||
>
|
||||
<option value="DIA0">Alle Messwerte</option>
|
||||
<option value="DIA1">Stündliche Werte</option>
|
||||
<option value="DIA2">Tägliche Werte</option>
|
||||
</select>
|
||||
if (isoDate >= vonDatum) dispatch(setBisDatum(isoDate));
|
||||
}}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
/>
|
||||
|
||||
{/* Dropdown für Slot-Typ */}
|
||||
<select
|
||||
value={selectedSlotType}
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
setSelectedSlotType(
|
||||
e.target.value as "isolationswiderstand" | "schleifenwiderstand"
|
||||
<select
|
||||
value={selectedMode}
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
setSelectedMode(e.target.value as "DIA0" | "DIA1" | "DIA2")
|
||||
)
|
||||
)
|
||||
}
|
||||
className="px-3 py-1 bg-white border rounded text-sm"
|
||||
>
|
||||
<option value="schleifenwiderstand">Schleifenwiderstand</option>
|
||||
<option value="isolationswiderstand">Isolationswiderstand</option>
|
||||
</select>
|
||||
}
|
||||
className="px-3 py-1 bg-white border rounded text-sm"
|
||||
>
|
||||
<option value="DIA0">Alle Messwerte</option>
|
||||
<option value="DIA1">Stündliche Werte</option>
|
||||
<option value="DIA2">Tägliche Werte</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
value={selectedSlotType}
|
||||
onChange={(e) =>
|
||||
dispatch(
|
||||
setSelectedSlotType(
|
||||
e.target.value as "isolationswiderstand" | "schleifenwiderstand"
|
||||
)
|
||||
)
|
||||
}
|
||||
className="px-3 py-1 bg-white border rounded text-sm"
|
||||
>
|
||||
<option value="schleifenwiderstand">Schleifenwiderstand</option>
|
||||
<option value="isolationswiderstand">Isolationswiderstand</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ const LoopMeasurementChart = () => {
|
||||
messwertMinimum: eintrag.i,
|
||||
messwertMaximum: eintrag.a,
|
||||
messwert: eintrag.m ?? null,
|
||||
messwertDurchschnitt: ["DIA1", "DIA2"].includes(selectedMode)
|
||||
messwertDurchschnitt: ["DIA0", "DIA1", "DIA2"].includes(selectedMode)
|
||||
? eintrag.g ?? null
|
||||
: null,
|
||||
}))
|
||||
@@ -110,15 +110,22 @@ const LoopMeasurementChart = () => {
|
||||
}, [formatierteDaten, brushRange.endIndex, dispatch]);
|
||||
|
||||
const handleBrushChange = useCallback(
|
||||
({ startIndex, endIndex }) => {
|
||||
({ startIndex, endIndex }: { startIndex?: number; endIndex?: number }) => {
|
||||
if (startIndex === undefined || endIndex === undefined) return; // Verhindert Fehler
|
||||
|
||||
dispatch(
|
||||
setBrushRange({
|
||||
startIndex,
|
||||
endIndex,
|
||||
startDate: new Date(formatierteDaten[startIndex].zeit)
|
||||
startDate: new Date(
|
||||
formatierteDaten[startIndex]?.zeit || formatierteDaten[0].zeit
|
||||
)
|
||||
.toISOString()
|
||||
.split("T")[0],
|
||||
endDate: new Date(formatierteDaten[endIndex].zeit)
|
||||
endDate: new Date(
|
||||
formatierteDaten[endIndex]?.zeit ||
|
||||
formatierteDaten[formatierteDaten.length - 1].zeit
|
||||
)
|
||||
.toISOString()
|
||||
.split("T")[0],
|
||||
})
|
||||
@@ -126,6 +133,7 @@ const LoopMeasurementChart = () => {
|
||||
},
|
||||
[dispatch, formatierteDaten]
|
||||
);
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
useEffect(() => {
|
||||
if (formatierteDaten.length) {
|
||||
|
||||
Reference in New Issue
Block a user