Refactor PlotHistoricalSession and TimePointSelector components for improved UI and functionality
- Removed the range display box from PlotHistoricalSession for a cleaner layout. - Updated TimePointSelector to support fullscreen mode with a more compact design. - Integrated a Popover for date and time selection in fullscreen mode. - Enhanced the DataAvailabilityBar and slider functionality for better user experience. - Added responsive styling adjustments for both normal and fullscreen modes. - Updated system state with the latest last_update timestamp.
This commit is contained in:
parent
61d61d27d1
commit
81e5ddec57
File diff suppressed because it is too large
Load Diff
|
@ -633,13 +633,6 @@ export default function PlotHistoricalSession({
|
|||
dataSegments={dataSegments}
|
||||
onTimeChange={handleTimePointChange}
|
||||
/>
|
||||
<Box mt={2} p={2} bg={infoBgColor} borderRadius="md" border="1px solid" borderColor={borderColor}>
|
||||
<HStack justify="space-between" fontSize="sm" color={textColor}>
|
||||
<Text><strong>Range:</strong> {timeRangeSeconds}s</Text>
|
||||
<Text><strong>{t('timeSelector.from')}:</strong> {formatCentralTimeInfo().start}</Text>
|
||||
<Text><strong>{t('timeSelector.to')}:</strong> {formatCentralTimeInfo().end}</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
|
@ -913,6 +906,7 @@ export default function PlotHistoricalSession({
|
|||
stepMinutes={1}
|
||||
dataSegments={dataSegments}
|
||||
onTimeChange={handleTimePointChange}
|
||||
isFullscreen={true}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,29 @@
|
|||
import { useMemo, useState, useCallback, useRef, useEffect } from "react";
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 {
|
||||
Box,
|
||||
Flex,
|
||||
Text,
|
||||
Slider,
|
||||
SliderTrack,
|
||||
SliderFilledTrack,
|
||||
SliderThumb,
|
||||
Button,
|
||||
IconButton,
|
||||
useColorModeValue,
|
||||
NumberInput,
|
||||
NumberInputField,
|
||||
NumberInputStepper,
|
||||
NumberIncrementStepper,
|
||||
NumberDecrementStepper,
|
||||
Popover,
|
||||
PopoverTrigger,
|
||||
PopoverContent,
|
||||
PopoverBody,
|
||||
PopoverArrow,
|
||||
HStack
|
||||
} from "@chakra-ui/react";
|
||||
import { CheckIcon, CalendarIcon } from "@chakra-ui/icons";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import DataAvailabilityBar from "./DataAvailabilityBar.jsx";
|
||||
|
@ -14,6 +36,7 @@ export default function TimePointSelector({
|
|||
stepMinutes = 5,
|
||||
dataSegments = [],
|
||||
onTimeChange,
|
||||
isFullscreen = false, // NEW: Enable ultra-compact mode for fullscreen
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
// Color mode values
|
||||
|
@ -162,211 +185,417 @@ export default function TimePointSelector({
|
|||
};
|
||||
|
||||
return (
|
||||
<Box p={4} bg={bgColor} borderRadius="md" border="1px solid" borderColor={borderColor}>
|
||||
<Flex gap={4} align="center" mb={3} wrap="wrap">
|
||||
<Box>
|
||||
<Text fontWeight="semibold" mb={1} color={textColor}>{t('timeSelector.selectDateTime')}</Text>
|
||||
<Box p={isFullscreen ? 2 : 3} bg={bgColor} borderRadius="md" borderWidth="1px" borderColor={borderColor}>
|
||||
{/* Compact header with date picker and controls */}
|
||||
{!isFullscreen && (
|
||||
<Flex gap={2} align="center" justify="space-between" mb={3}>
|
||||
{/* Compact Date/Time Picker */}
|
||||
<Popover placement="bottom-start">
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
leftIcon={<CalendarIcon />}
|
||||
title="Select date and time"
|
||||
>
|
||||
📅 {value.toLocaleString('en-US', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverArrow />
|
||||
<PopoverBody p={0}>
|
||||
<Box
|
||||
sx={{
|
||||
'& .react-datepicker': {
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '14px',
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
boxShadow: useColorModeValue('md', 'dark-lg'),
|
||||
display: 'flex',
|
||||
flexDirection: 'row' // Force horizontal layout
|
||||
},
|
||||
'& .react-datepicker__header': {
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700'),
|
||||
borderBottom: '1px solid',
|
||||
borderColor: borderColor,
|
||||
height: '40px' // Fixed header height
|
||||
},
|
||||
'& .react-datepicker__current-month': {
|
||||
color: textColor,
|
||||
lineHeight: '40px' // Center text in header
|
||||
},
|
||||
'& .react-datepicker__day-names': {
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
'& .react-datepicker__day-name': {
|
||||
color: textColor,
|
||||
height: '30px',
|
||||
lineHeight: '30px'
|
||||
},
|
||||
'& .react-datepicker__week': {
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
'& .react-datepicker__day': {
|
||||
color: textColor,
|
||||
height: '30px',
|
||||
lineHeight: '30px',
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('blue.50', 'blue.800')
|
||||
}
|
||||
},
|
||||
'& .react-datepicker__day--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white'
|
||||
},
|
||||
'& .react-datepicker__month': {
|
||||
height: '210px' // 6 weeks × 30px + 30px for day names
|
||||
},
|
||||
// Time picker specific styles - match calendar height exactly
|
||||
'& .react-datepicker__time-container': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
borderLeft: '1px solid',
|
||||
borderColor: borderColor,
|
||||
width: '100px',
|
||||
flexShrink: 0,
|
||||
height: '250px', // Match total calendar height (40px header + 210px month)
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
'& .react-datepicker__time-box': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
'& .react-datepicker__time-name': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700'),
|
||||
textAlign: 'center',
|
||||
padding: '8px',
|
||||
height: '40px',
|
||||
lineHeight: '24px',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: borderColor,
|
||||
flexShrink: 0
|
||||
},
|
||||
'& .react-datepicker__time-list': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
height: '210px', // Match month container height exactly
|
||||
maxHeight: '210px',
|
||||
overflowY: 'auto',
|
||||
flex: 1,
|
||||
'& .react-datepicker__time-list-item': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
padding: '4px 8px',
|
||||
fontSize: '13px',
|
||||
lineHeight: '1.4',
|
||||
height: '26px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('gray.100', 'gray.700')
|
||||
},
|
||||
'&--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Ensure calendar and time picker are side by side
|
||||
'& .react-datepicker__month-container': {
|
||||
float: 'none',
|
||||
display: 'inline-block',
|
||||
height: '250px' // Match time container height
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
selected={value}
|
||||
onChange={onPick}
|
||||
showTimeSelect
|
||||
timeFormat="HH:mm"
|
||||
timeIntervals={stepMinutes}
|
||||
timeCaption="Time"
|
||||
dateFormat="dd-MM-yyyy HH:mm"
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
minTime={new Date(new Date(value).setHours(0, 0, 0, 0))}
|
||||
maxTime={new Date(new Date(value).setHours(23, 59, 59, 999))}
|
||||
inline
|
||||
/>
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
{/* Apply button when there are pending changes */}
|
||||
{hasPendingChanges && (
|
||||
<Button
|
||||
size="sm"
|
||||
colorScheme="blue"
|
||||
leftIcon={<CheckIcon />}
|
||||
onClick={applyPendingChanges}
|
||||
>
|
||||
{t('timeSelector.applyChanges')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* Time navigation slider with data availability */}
|
||||
<Box mb={isFullscreen ? 1 : 2}>
|
||||
<Box position="relative">
|
||||
{/* Data availability bar positioned above slider track */}
|
||||
<Box
|
||||
sx={{
|
||||
'& .react-datepicker-wrapper': {
|
||||
width: 'auto'
|
||||
},
|
||||
'& .react-datepicker__input-container input': {
|
||||
padding: '8px 12px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid',
|
||||
borderColor: borderColor,
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
color: textColor,
|
||||
fontSize: '14px',
|
||||
'&:focus': {
|
||||
outline: 'none',
|
||||
borderColor: 'blue.500',
|
||||
boxShadow: '0 0 0 1px blue.500'
|
||||
}
|
||||
},
|
||||
'& .react-datepicker': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
border: '1px solid',
|
||||
borderColor: borderColor,
|
||||
color: textColor
|
||||
},
|
||||
'& .react-datepicker__header': {
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700'),
|
||||
borderBottom: '1px solid',
|
||||
borderColor: borderColor
|
||||
},
|
||||
'& .react-datepicker__day': {
|
||||
color: textColor,
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('blue.50', 'blue.800')
|
||||
}
|
||||
},
|
||||
'& .react-datepicker__day--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white'
|
||||
},
|
||||
// Time picker specific styles
|
||||
'& .react-datepicker__time-container': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
borderLeft: '1px solid',
|
||||
borderColor: borderColor
|
||||
},
|
||||
'& .react-datepicker__time-box': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800')
|
||||
},
|
||||
'& .react-datepicker__time-list': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
'& .react-datepicker__time-list-item': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('gray.100', 'gray.700')
|
||||
},
|
||||
'&--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
},
|
||||
'& .react-datepicker__time-name': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700')
|
||||
}
|
||||
}}
|
||||
position="absolute"
|
||||
top="-8px"
|
||||
left="0"
|
||||
right="0"
|
||||
px="1"
|
||||
zIndex={1}
|
||||
>
|
||||
<DatePicker
|
||||
selected={value}
|
||||
onChange={onPick}
|
||||
showTimeSelect
|
||||
timeFormat="HH:mm"
|
||||
timeIntervals={stepMinutes}
|
||||
timeCaption="Time"
|
||||
dateFormat="dd-MM-yyyy HH:mm"
|
||||
<DataAvailabilityBar
|
||||
segments={dataSegments}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
// Optional: force time range when navigating
|
||||
minTime={new Date(new Date(value).setHours(0, 0, 0, 0))}
|
||||
maxTime={new Date(new Date(value).setHours(23, 59, 59, 999))}
|
||||
height="4px"
|
||||
showTooltips={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Slider
|
||||
min={minMs}
|
||||
max={maxMs}
|
||||
step={stepMs}
|
||||
value={sliderValue}
|
||||
onChange={onSlide}
|
||||
colorScheme="blue"
|
||||
size={isFullscreen ? "sm" : "md"}
|
||||
>
|
||||
<SliderTrack bg={useColorModeValue('gray.200', 'gray.600')}>
|
||||
<SliderFilledTrack bg="blue.500" />
|
||||
</SliderTrack>
|
||||
<SliderThumb bg="blue.500" />
|
||||
</Slider>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box flex="1" minW="260px">
|
||||
<Text color={textColor} mb={1}>{t('timeSelector.navigateSlider')}</Text>
|
||||
|
||||
{/* Slider with integrated data availability */}
|
||||
<Box position="relative" mb={2}>
|
||||
{/* Data availability bar positioned above slider track */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="-8px"
|
||||
left="0"
|
||||
right="0"
|
||||
px="1"
|
||||
zIndex={1}
|
||||
>
|
||||
<DataAvailabilityBar
|
||||
segments={dataSegments}
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
height="4px"
|
||||
showTooltips={true}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Slider
|
||||
min={minMs}
|
||||
max={maxMs}
|
||||
step={stepMs}
|
||||
value={sliderValue}
|
||||
onChange={onSlide}
|
||||
colorScheme="blue"
|
||||
>
|
||||
<SliderTrack bg={useColorModeValue('gray.200', 'gray.600')}>
|
||||
<SliderFilledTrack bg="blue.500" />
|
||||
</SliderTrack>
|
||||
<SliderThumb bg="blue.500" />
|
||||
</Slider>
|
||||
</Box>
|
||||
|
||||
<Flex mt={2} justify="space-between" align="center">
|
||||
<Text fontSize="sm" color={textColor}>
|
||||
{new Date(sliderValue).toLocaleString('en-US', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})}
|
||||
{/* Compact footer with editable range and status */}
|
||||
<Flex justify="space-between" align="center" wrap="wrap" gap={2} fontSize={isFullscreen ? "xs" : "sm"}>
|
||||
{/* Editable Range Display */}
|
||||
<HStack spacing={1} align="center">
|
||||
<Text color={useColorModeValue('gray.600', 'gray.400')}>
|
||||
<strong>Range:</strong>
|
||||
</Text>
|
||||
<NumberInput
|
||||
value={tempRangeSeconds}
|
||||
onChange={(valueString, valueNumber) => onRangeChange(isNaN(valueNumber) ? 1000 : valueNumber)}
|
||||
min={60}
|
||||
max={86400}
|
||||
size="xs"
|
||||
width={isFullscreen ? "50px" : "60px"}
|
||||
>
|
||||
<NumberInputField
|
||||
fontSize={isFullscreen ? "xs" : "sm"}
|
||||
px={1}
|
||||
py={isFullscreen ? "1px" : undefined}
|
||||
bg={useColorModeValue('white', 'gray.700')}
|
||||
borderColor={useColorModeValue('gray.300', 'gray.600')}
|
||||
/>
|
||||
</NumberInput>
|
||||
<Text color={useColorModeValue('gray.600', 'gray.400')}>s</Text>
|
||||
</HStack>
|
||||
|
||||
{/* Ultra-compact controls for fullscreen */}
|
||||
{isFullscreen ? (
|
||||
<HStack spacing={2} fontSize="xs">
|
||||
{/* Minimal date picker button for fullscreen */}
|
||||
<Popover placement="bottom">
|
||||
<PopoverTrigger>
|
||||
<IconButton
|
||||
size="xs"
|
||||
variant="outline"
|
||||
icon={<CalendarIcon />}
|
||||
title="Select date and time"
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverArrow />
|
||||
<PopoverBody p={0}>
|
||||
<Box
|
||||
sx={{
|
||||
'& .react-datepicker': {
|
||||
fontFamily: 'inherit',
|
||||
fontSize: '14px',
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
border: 'none',
|
||||
borderRadius: '6px',
|
||||
boxShadow: useColorModeValue('md', 'dark-lg'),
|
||||
display: 'flex',
|
||||
flexDirection: 'row' // Force horizontal layout
|
||||
},
|
||||
'& .react-datepicker__header': {
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700'),
|
||||
borderBottom: '1px solid',
|
||||
borderColor: borderColor,
|
||||
height: '40px' // Fixed header height
|
||||
},
|
||||
'& .react-datepicker__current-month': {
|
||||
color: textColor,
|
||||
lineHeight: '40px' // Center text in header
|
||||
},
|
||||
'& .react-datepicker__day-names': {
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
'& .react-datepicker__day-name': {
|
||||
color: textColor,
|
||||
height: '30px',
|
||||
lineHeight: '30px'
|
||||
},
|
||||
'& .react-datepicker__week': {
|
||||
height: '30px',
|
||||
display: 'flex',
|
||||
alignItems: 'center'
|
||||
},
|
||||
'& .react-datepicker__day': {
|
||||
color: textColor,
|
||||
height: '30px',
|
||||
lineHeight: '30px',
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('blue.50', 'blue.800')
|
||||
}
|
||||
},
|
||||
'& .react-datepicker__day--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white'
|
||||
},
|
||||
'& .react-datepicker__month': {
|
||||
height: '210px' // 6 weeks × 30px + 30px for day names
|
||||
},
|
||||
// Time picker specific styles - match calendar height exactly
|
||||
'& .react-datepicker__time-container': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
borderLeft: '1px solid',
|
||||
borderColor: borderColor,
|
||||
width: '100px',
|
||||
flexShrink: 0,
|
||||
height: '250px', // Match total calendar height (40px header + 210px month)
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
'& .react-datepicker__time-box': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
'& .react-datepicker__time-name': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('gray.50', 'gray.700'),
|
||||
textAlign: 'center',
|
||||
padding: '8px',
|
||||
height: '40px',
|
||||
lineHeight: '24px',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: borderColor,
|
||||
flexShrink: 0
|
||||
},
|
||||
'& .react-datepicker__time-list': {
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
height: '210px', // Match month container height exactly
|
||||
maxHeight: '210px',
|
||||
overflowY: 'auto',
|
||||
flex: 1,
|
||||
'& .react-datepicker__time-list-item': {
|
||||
color: textColor,
|
||||
backgroundColor: useColorModeValue('white', 'gray.800'),
|
||||
padding: '4px 8px',
|
||||
fontSize: '13px',
|
||||
lineHeight: '1.4',
|
||||
height: '26px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
'&:hover': {
|
||||
backgroundColor: useColorModeValue('gray.100', 'gray.700')
|
||||
},
|
||||
'&--selected': {
|
||||
backgroundColor: 'blue.500',
|
||||
color: 'white',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
}
|
||||
},
|
||||
// Ensure calendar and time picker are side by side
|
||||
'& .react-datepicker__month-container': {
|
||||
float: 'none',
|
||||
display: 'inline-block',
|
||||
height: '250px' // Match time container height
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DatePicker
|
||||
selected={value}
|
||||
onChange={onPick}
|
||||
showTimeSelect
|
||||
timeFormat="HH:mm"
|
||||
timeIntervals={stepMinutes}
|
||||
timeCaption="Time"
|
||||
dateFormat="dd-MM-yyyy HH:mm"
|
||||
minDate={minDate}
|
||||
maxDate={maxDate}
|
||||
minTime={new Date(new Date(value).setHours(0, 0, 0, 0))}
|
||||
maxTime={new Date(new Date(value).setHours(23, 59, 59, 999))}
|
||||
inline
|
||||
/>
|
||||
</Box>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{hasPendingChanges && (
|
||||
<Button
|
||||
size="xs"
|
||||
colorScheme="blue"
|
||||
leftIcon={<CheckIcon />}
|
||||
onClick={applyPendingChanges}
|
||||
>
|
||||
Apply
|
||||
</Button>
|
||||
)}
|
||||
</HStack>
|
||||
) : (
|
||||
/* Normal mode - Step info and pending changes status */
|
||||
<HStack spacing={3} fontSize="xs">
|
||||
<Text color={useColorModeValue('gray.500', 'gray.400')}>
|
||||
Step: {stepMinutes} min
|
||||
</Text>
|
||||
{hasPendingChanges && (
|
||||
<Text fontSize="xs" color="orange.500">
|
||||
<Text color="orange.500" fontWeight="medium">
|
||||
{t('timeSelector.pendingChanges')}
|
||||
</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}>
|
||||
{t('timeSelector.timeRangeSeconds')}
|
||||
</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}>
|
||||
{t('timeSelector.pendingChanges')}
|
||||
</Text>
|
||||
<Button
|
||||
size="sm"
|
||||
colorScheme="blue"
|
||||
leftIcon={<CheckIcon />}
|
||||
onClick={applyPendingChanges}
|
||||
>
|
||||
{t('timeSelector.applyChanges')}
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
</Box>
|
||||
</HStack>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Text fontSize="sm" color={useColorModeValue('gray.500', 'gray.400')}>
|
||||
Range: {minDate.toLocaleString('en-US', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})} → {maxDate.toLocaleString('en-US', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: 'numeric',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
hour12: false
|
||||
})} | Step: {stepMinutes} min
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,5 @@
|
|||
]
|
||||
},
|
||||
"auto_recovery_enabled": true,
|
||||
"last_update": "2025-08-27T12:48:41.182586",
|
||||
"plotjuggler_path": "C:\\Program Files\\PlotJuggler\\plotjuggler.exe"
|
||||
"last_update": "2025-08-27T15:19:56.923648"
|
||||
}
|
Loading…
Reference in New Issue