feat: Enhance TimePointSelector with range input and apply changes functionality

- Updated PlotHistoricalSession to handle time range changes.
- Modified TimePointSelector to include a new range input for selecting time range in seconds.
- Implemented state management for the new range input and synchronized it with time changes.
- Added apply changes button to confirm both time and range adjustments.
- Improved logging for better debugging and tracking of changes.
This commit is contained in:
Miguel 2025-08-16 19:42:26 +02:00
parent 0f928c50e7
commit 3803cc92ae
3 changed files with 3073 additions and 2020 deletions

File diff suppressed because it is too large Load Diff

View File

@ -418,9 +418,12 @@ export default function PlotHistoricalSession({
}
// Handle time change from TimePointSelector
const handleTimePointChange = (newCentralTime) => {
console.log('📊 Time selector change:', newCentralTime)
const handleTimePointChange = (newCentralTime, newRangeSeconds = null) => {
console.log('📊 Time selector change:', { newCentralTime, newRangeSeconds })
setCentralTime(newCentralTime)
if (newRangeSeconds !== null) {
setTimeRangeSeconds(newRangeSeconds)
}
}
// Color mode
@ -586,6 +589,7 @@ export default function PlotHistoricalSession({
minDate={dateRange.minDate}
maxDate={dateRange.maxDate}
initial={centralTime}
initialRangeSeconds={timeRangeSeconds}
stepMinutes={1}
dataSegments={dataSegments}
onTimeChange={handleTimePointChange}

View File

@ -1,5 +1,5 @@
import { useMemo, useState, useCallback, useRef, useEffect } from "react";
import { Box, Flex, Text, Slider, SliderTrack, SliderFilledTrack, SliderThumb, Button, IconButton, useColorModeValue } from "@chakra-ui/react";
import { Box, Flex, Text, Slider, SliderTrack, SliderFilledTrack, SliderThumb, Button, IconButton, useColorModeValue, NumberInput, NumberInputField, NumberInputStepper, NumberIncrementStepper, NumberDecrementStepper } from "@chakra-ui/react";
import { CheckIcon } from "@chakra-ui/icons";
import DatePicker from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
@ -9,6 +9,7 @@ export default function TimePointSelector({
minDate,
maxDate,
initial,
initialRangeSeconds = 1000,
stepMinutes = 5,
dataSegments = [],
onTimeChange,
@ -32,6 +33,10 @@ export default function TimePointSelector({
// Estado temporal del slider (solo para UI, no dispara eventos)
const [sliderValue, setSliderValue] = useState(() => value.getTime());
// Estado para el range temporal (para UI y apply)
const [rangeSeconds, setRangeSeconds] = useState(initialRangeSeconds);
const [tempRangeSeconds, setTempRangeSeconds] = useState(initialRangeSeconds);
// Estado para detectar si hay cambios pendientes
const [hasPendingChanges, setHasPendingChanges] = useState(false);
@ -57,6 +62,14 @@ export default function TimePointSelector({
}, 0);
}, [initial, minMs, maxMs]);
// Sincronizar con cambios externos en el range
useEffect(() => {
if (!isExternalUpdateRef.current) return; // Solo para actualizaciones externas
setRangeSeconds(initialRangeSeconds);
setTempRangeSeconds(initialRangeSeconds);
}, [initialRangeSeconds]);
const valueMs = value.getTime();
// Redondea al paso del slider
@ -94,19 +107,20 @@ export default function TimePointSelector({
};
}, []);
// Función para aplicar los cambios pendientes del slider
const applySliderChanges = useCallback(() => {
// Función para aplicar los cambios pendientes (tiempo y/o range)
const applyPendingChanges = useCallback(() => {
if (!hasPendingChanges) return;
const newValue = new Date(sliderValue);
setValue(newValue);
setRangeSeconds(tempRangeSeconds);
setHasPendingChanges(false);
if (onTimeChange) {
console.log('📊 TimeSelector: Applying slider changes', newValue);
onTimeChange(newValue);
console.log('📊 TimeSelector: Applying changes', { time: newValue, range: tempRangeSeconds });
onTimeChange(newValue, tempRangeSeconds);
}
}, [sliderValue, hasPendingChanges, onTimeChange]);
}, [sliderValue, tempRangeSeconds, hasPendingChanges, onTimeChange]);
// Cambio desde el DatePicker (inmediato, actualiza ambos valores)
const onPick = (d) => {
@ -117,10 +131,10 @@ export default function TimePointSelector({
setSliderValue(newValue.getTime());
setHasPendingChanges(false);
// DatePicker es cambio directo
// DatePicker es cambio directo - usar range actual
if (onTimeChange) {
console.log('📊 TimeSelector: DatePicker change (immediate)', newValue);
onTimeChange(newValue);
onTimeChange(newValue, rangeSeconds);
}
};
@ -129,7 +143,20 @@ export default function TimePointSelector({
if (isExternalUpdateRef.current) return;
setSliderValue(ms);
setHasPendingChanges(Math.abs(ms - value.getTime()) > 1000); // Solo marcar si hay diferencia significativa
checkForPendingChanges(ms, tempRangeSeconds);
};
// Cambio en el range input
const onRangeChange = (newRangeSeconds) => {
setTempRangeSeconds(newRangeSeconds);
checkForPendingChanges(sliderValue, newRangeSeconds);
};
// Función para verificar si hay cambios pendientes
const checkForPendingChanges = (currentSliderValue, currentRangeSeconds) => {
const timeChanged = Math.abs(currentSliderValue - value.getTime()) > 1000;
const rangeChanged = Math.abs(currentRangeSeconds - rangeSeconds) > 0;
setHasPendingChanges(timeChanged || rangeChanged);
};
return (
@ -197,20 +224,7 @@ export default function TimePointSelector({
</Box>
<Box flex="1" minW="260px">
<Flex align="center" mb={1}>
<Text color={textColor}>Navigate with slider</Text>
{hasPendingChanges && (
<Button
size="xs"
ml={2}
colorScheme="blue"
leftIcon={<CheckIcon />}
onClick={applySliderChanges}
>
Apply
</Button>
)}
</Flex>
<Text color={textColor} mb={1}>Navigate with slider</Text>
{/* Slider with integrated data availability */}
<Box position="relative" mb={2}>
@ -264,6 +278,45 @@ export default function TimePointSelector({
</Text>
)}
</Flex>
{/* Range and Apply Controls */}
<Flex mt={3} gap={3} align="center" wrap="wrap">
<Box>
<Text fontSize="sm" fontWeight="medium" color={textColor} mb={1}>
Time Range (seconds)
</Text>
<NumberInput
value={tempRangeSeconds}
onChange={(valueString, valueNumber) => onRangeChange(isNaN(valueNumber) ? 1000 : valueNumber)}
min={60}
max={86400}
size="sm"
width="120px"
>
<NumberInputField />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
</Box>
{hasPendingChanges && (
<Box>
<Text fontSize="sm" color="orange.500" mb={1}>
Pending changes
</Text>
<Button
size="sm"
colorScheme="blue"
leftIcon={<CheckIcon />}
onClick={applyPendingChanges}
>
Apply Changes
</Button>
</Box>
)}
</Flex>
</Box>
</Flex>