Refactor PlotHistoricalManager: Remove TimeRangeSelector and manage time range directly in session creation; update PlotHistoricalSession for improved performance with simple plot mode toggle; enhance TimePointSelector with time picker styles for better UI consistency.
This commit is contained in:
parent
c3c55dd3dc
commit
e46cc62a0d
File diff suppressed because it is too large
Load Diff
|
@ -94,145 +94,6 @@ function ConfirmationDialog({ isOpen, onClose, onConfirm, title, message, itemNa
|
|||
)
|
||||
}
|
||||
|
||||
// Time Range Selector Component
|
||||
function TimeRangeSelector({ startTime, endTime, onTimeRangeChange, isLoading }) {
|
||||
const [localStartTime, setLocalStartTime] = useState('')
|
||||
const [localEndTime, setLocalEndTime] = useState('')
|
||||
const toast = useToast()
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize with current values with safe date handling
|
||||
if (startTime) {
|
||||
const startDate = new Date(startTime)
|
||||
if (!isNaN(startDate.getTime())) {
|
||||
setLocalStartTime(startDate.toISOString().slice(0, 16))
|
||||
}
|
||||
}
|
||||
if (endTime) {
|
||||
const endDate = new Date(endTime)
|
||||
if (!isNaN(endDate.getTime())) {
|
||||
setLocalEndTime(endDate.toISOString().slice(0, 16))
|
||||
}
|
||||
}
|
||||
}, [startTime, endTime])
|
||||
|
||||
const handleApplyTimeRange = () => {
|
||||
if (localStartTime && localEndTime) {
|
||||
const start = new Date(localStartTime)
|
||||
const end = new Date(localEndTime)
|
||||
|
||||
// Validate the time range
|
||||
if (start >= end) {
|
||||
toast({
|
||||
title: "Invalid Time Range",
|
||||
description: "Start time must be before end time",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
isClosable: true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
|
||||
toast({
|
||||
title: "Invalid Date",
|
||||
description: "Please enter valid dates",
|
||||
status: "error",
|
||||
duration: 3000,
|
||||
isClosable: true
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
onTimeRangeChange(start, end)
|
||||
}
|
||||
}
|
||||
|
||||
const handleQuickRange = (hours) => {
|
||||
const end = new Date()
|
||||
const start = new Date(end.getTime() - hours * 60 * 60 * 1000)
|
||||
|
||||
// Update local state
|
||||
setLocalStartTime(start.toISOString().slice(0, 16))
|
||||
setLocalEndTime(end.toISOString().slice(0, 16))
|
||||
|
||||
// Immediately apply the time range
|
||||
onTimeRangeChange(start, end)
|
||||
|
||||
toast({
|
||||
title: "Time Range Updated",
|
||||
description: `Set to last ${hours} hour${hours !== 1 ? 's' : ''}`,
|
||||
status: "info",
|
||||
duration: 2000,
|
||||
isClosable: true
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Card mb={4}>
|
||||
<CardHeader>
|
||||
<Heading size="sm">📅 Time Range Selection</Heading>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<VStack spacing={4} align="stretch">
|
||||
<HStack spacing={4}>
|
||||
<FormControl>
|
||||
<FormLabel fontSize="sm">Start Time</FormLabel>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
value={localStartTime}
|
||||
onChange={(e) => setLocalStartTime(e.target.value)}
|
||||
size="sm"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel fontSize="sm">End Time</FormLabel>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
value={localEndTime}
|
||||
onChange={(e) => setLocalEndTime(e.target.value)}
|
||||
size="sm"
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
colorScheme="blue"
|
||||
onClick={handleApplyTimeRange}
|
||||
isLoading={isLoading}
|
||||
size="sm"
|
||||
mt={6}
|
||||
>
|
||||
📊 Apply
|
||||
</Button>
|
||||
</HStack>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Box>
|
||||
<Text fontSize="sm" mb={2} fontWeight="medium">Quick Ranges:</Text>
|
||||
<HStack spacing={2} flexWrap="wrap">
|
||||
<Button size="xs" onClick={() => handleQuickRange(1)} isLoading={isLoading}>
|
||||
Last Hour
|
||||
</Button>
|
||||
<Button size="xs" onClick={() => handleQuickRange(6)} isLoading={isLoading}>
|
||||
Last 6 Hours
|
||||
</Button>
|
||||
<Button size="xs" onClick={() => handleQuickRange(24)} isLoading={isLoading}>
|
||||
Last 24 Hours
|
||||
</Button>
|
||||
<Button size="xs" onClick={() => handleQuickRange(72)} isLoading={isLoading}>
|
||||
Last 3 Days
|
||||
</Button>
|
||||
<Button size="xs" onClick={() => handleQuickRange(168)} isLoading={isLoading}>
|
||||
Last Week
|
||||
</Button>
|
||||
</HStack>
|
||||
</Box>
|
||||
</VStack>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
// Collapsible Plot Items Form - Each item in the array is individually collapsible
|
||||
function CollapsiblePlotItemsForm({ data, schema, uiSchema, onSave, title, icon, getItemLabel, isExpanded, onToggleExpansion }) {
|
||||
const [formData, setFormData] = useState(data)
|
||||
|
@ -431,14 +292,6 @@ export default function PlotHistoricalManager() {
|
|||
const [booleanVariables, setBooleanVariables] = useState([])
|
||||
|
||||
const [selectedPlotId, setSelectedPlotId] = useState('')
|
||||
const [customTimeRange, setCustomTimeRange] = useState(() => {
|
||||
const now = new Date()
|
||||
const twentyFourHoursAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000)
|
||||
return {
|
||||
start: twentyFourHoursAgo,
|
||||
end: now
|
||||
}
|
||||
})
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isLoadingHistoricalData, setIsLoadingHistoricalData] = useState(false)
|
||||
|
@ -641,16 +494,22 @@ export default function PlotHistoricalManager() {
|
|||
// Create historical session
|
||||
const sessionId = `historical_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
|
||||
|
||||
// Default time range - individual charts will manage their own time ranges
|
||||
const defaultTimeRange = {
|
||||
start: new Date(Date.now() - 24 * 60 * 60 * 1000), // 24 hours ago
|
||||
end: new Date()
|
||||
}
|
||||
|
||||
const newSession = {
|
||||
id: sessionId,
|
||||
plotId: selectedPlotId,
|
||||
name: plotDef.name,
|
||||
variables: variableNames,
|
||||
timeRange: { ...customTimeRange },
|
||||
timeRange: defaultTimeRange,
|
||||
config: {
|
||||
...plotDef,
|
||||
variables: variableNames,
|
||||
time_window: Math.floor((customTimeRange.end - customTimeRange.start) / 1000),
|
||||
time_window: 86400, // 24 hours in seconds
|
||||
y_min: plotDef.y_min,
|
||||
y_max: plotDef.y_max,
|
||||
trigger_variable: plotDef.trigger_variable,
|
||||
|
@ -696,11 +555,6 @@ export default function PlotHistoricalManager() {
|
|||
})
|
||||
}
|
||||
|
||||
const handleTimeRangeChange = (start, end) => {
|
||||
console.log('📅 Time range changed:', { start, end })
|
||||
setCustomTimeRange({ start, end })
|
||||
}
|
||||
|
||||
const bgColor = useColorModeValue('gray.50', 'gray.900')
|
||||
const cardBgColor = useColorModeValue('white', 'gray.800')
|
||||
|
||||
|
@ -758,14 +612,6 @@ export default function PlotHistoricalManager() {
|
|||
</CardHeader>
|
||||
</Card>
|
||||
|
||||
{/* Time Range Selector */}
|
||||
<TimeRangeSelector
|
||||
startTime={customTimeRange.start}
|
||||
endTime={customTimeRange.end}
|
||||
onTimeRangeChange={handleTimeRangeChange}
|
||||
isLoading={isLoadingHistoricalData}
|
||||
/>
|
||||
|
||||
{/* Plot Creation */}
|
||||
<Card bg={cardBgColor}>
|
||||
<CardHeader>
|
||||
|
@ -852,36 +698,6 @@ export default function PlotHistoricalManager() {
|
|||
</Collapse>
|
||||
</Card>
|
||||
|
||||
{/* Plot Definitions Configuration */}
|
||||
{plotDefinitionsSchema && (
|
||||
<CollapsiblePlotItemsForm
|
||||
data={plotDefinitions.plots || []}
|
||||
schema={plotDefinitionsSchema}
|
||||
uiSchema={plotDefinitionsUiSchema}
|
||||
onSave={(newPlots) => savePlotDefinitions({ plots: newPlots })}
|
||||
title="Plot Definitions"
|
||||
icon="📊"
|
||||
getItemLabel={(item) => item.name || 'Unnamed Plot'}
|
||||
isExpanded={isDefinitionsExpanded}
|
||||
onToggleExpansion={() => setIsDefinitionsExpanded(!isDefinitionsExpanded)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Plot Variables Configuration */}
|
||||
{plotVariablesSchema && (
|
||||
<CollapsiblePlotItemsForm
|
||||
data={plotVariables.variables || []}
|
||||
schema={plotVariablesSchema}
|
||||
uiSchema={plotVariablesUiSchema}
|
||||
onSave={(newVariables) => savePlotVariables({ variables: newVariables })}
|
||||
title="Plot Variables"
|
||||
icon="🔢"
|
||||
getItemLabel={(item) => `${item.plot_id}: ${item.variable_name}` || 'Unnamed Variable'}
|
||||
isExpanded={isVariablesExpanded}
|
||||
onToggleExpansion={() => setIsVariablesExpanded(!isVariablesExpanded)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isLoading && (
|
||||
<Flex justify="center" py={8}>
|
||||
<Spinner size="lg" color="blue.500" />
|
||||
|
|
|
@ -46,7 +46,7 @@ import {
|
|||
Progress,
|
||||
Tooltip
|
||||
} from '@chakra-ui/react'
|
||||
import { SettingsIcon, RepeatIcon, ViewIcon, DeleteIcon, TimeIcon, CalendarIcon, ViewOffIcon } from '@chakra-ui/icons'
|
||||
import { SettingsIcon, RepeatIcon, ViewIcon, DeleteIcon, TimeIcon, CalendarIcon, ViewOffIcon, StarIcon, ExternalLinkIcon, ChevronUpIcon, ChevronDownIcon } from '@chakra-ui/icons'
|
||||
import ChartjsHistoricalPlot from './ChartjsHistoricalPlot.jsx'
|
||||
import TimePointSelector from './TimePointSelector.jsx'
|
||||
import DataAvailabilityBar from './DataAvailabilityBar.jsx'
|
||||
|
@ -171,8 +171,8 @@ export default function PlotHistoricalSession({
|
|||
const { isOpen: isConfigModalOpen, onOpen: onConfigModalOpen, onClose: onConfigModalClose } = useDisclosure()
|
||||
const { isOpen: isFullscreen, onOpen: openFullscreen, onClose: closeFullscreen } = useDisclosure()
|
||||
|
||||
// NEW: Simple plot mode toggle
|
||||
const [isSimplePlot, setIsSimplePlot] = useState(false)
|
||||
// NEW: Simple plot mode toggle (default to true for better performance)
|
||||
const [isSimplePlot, setIsSimplePlot] = useState(true)
|
||||
|
||||
// Keep track of the last loaded data range for optimization
|
||||
const [loadedDataRange, setLoadedDataRange] = useState(null)
|
||||
|
@ -438,6 +438,17 @@ export default function PlotHistoricalSession({
|
|||
const infoBgColor = useColorModeValue('gray.50', 'gray.700')
|
||||
const subtleTextColor = useColorModeValue('gray.500', 'gray.400')
|
||||
const smallTextColor = useColorModeValue('gray.400', 'gray.500')
|
||||
|
||||
// Additional color mode values for conditional elements
|
||||
const whiteAlphaBg = useColorModeValue('whiteAlpha.800', 'blackAlpha.800')
|
||||
const inputBg = useColorModeValue('white', 'gray.700')
|
||||
const inputHoverBg = useColorModeValue('gray.50', 'gray.600')
|
||||
const editFieldColor = useColorModeValue('gray.900', 'gray.100')
|
||||
const editFieldBg = useColorModeValue('gray.50', 'gray.600')
|
||||
const calendarFilter = useColorModeValue('none', 'invert(1)')
|
||||
const modalBg = useColorModeValue('white', 'gray.700')
|
||||
const modalTextColor = useColorModeValue('gray.600', 'gray.300')
|
||||
const highlightBg = useColorModeValue('blue.50', 'blue.900')
|
||||
|
||||
// Format time range for display
|
||||
const formatTimeRange = (timeRange) => {
|
||||
|
@ -552,20 +563,20 @@ export default function PlotHistoricalSession({
|
|||
colorScheme={showDataPreview ? 'blue' : 'gray'}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="Simple Plot Mode">
|
||||
<Tooltip label={isSimplePlot ? "Standard Plot Mode" : "Simple Plot Mode"}>
|
||||
<IconButton
|
||||
icon={<ViewOffIcon />}
|
||||
icon={isSimplePlot ? <StarIcon /> : <ViewOffIcon />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setIsSimplePlot(!isSimplePlot)}
|
||||
colorScheme={isSimplePlot ? 'blue' : 'gray'}
|
||||
colorScheme={isSimplePlot ? 'orange' : 'gray'}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip label="Fullscreen">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
leftIcon={<ViewIcon />}
|
||||
leftIcon={<ExternalLinkIcon />}
|
||||
onClick={openFullscreen}
|
||||
>
|
||||
Fullscreen
|
||||
|
@ -582,7 +593,7 @@ export default function PlotHistoricalSession({
|
|||
</Tooltip>
|
||||
<Tooltip label={isExpanded ? "Collapse" : "Expand"}>
|
||||
<IconButton
|
||||
icon={isExpanded ? <ViewIcon /> : <ViewIcon />}
|
||||
icon={isExpanded ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => setIsExpanded(!isExpanded)}
|
||||
|
@ -687,7 +698,7 @@ export default function PlotHistoricalSession({
|
|||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
bg={useColorModeValue('whiteAlpha.800', 'blackAlpha.800')}
|
||||
bg={whiteAlphaBg}
|
||||
backdropFilter="blur(2px)"
|
||||
>
|
||||
<VStack>
|
||||
|
@ -728,6 +739,34 @@ export default function PlotHistoricalSession({
|
|||
}
|
||||
onChange={(e) => setCentralTime(new Date(e.target.value))}
|
||||
size="sm"
|
||||
bg={inputBg}
|
||||
_hover={{ bg: inputHoverBg }}
|
||||
sx={{
|
||||
'&::-webkit-datetime-edit': {
|
||||
color: editFieldColor
|
||||
},
|
||||
'&::-webkit-datetime-edit-fields-wrapper': {
|
||||
background: 'transparent'
|
||||
},
|
||||
'&::-webkit-datetime-edit-hour-field': {
|
||||
background: editFieldBg,
|
||||
borderRadius: '2px',
|
||||
padding: '1px 2px'
|
||||
},
|
||||
'&::-webkit-datetime-edit-minute-field': {
|
||||
background: editFieldBg,
|
||||
borderRadius: '2px',
|
||||
padding: '1px 2px'
|
||||
},
|
||||
'&::-webkit-datetime-edit-second-field': {
|
||||
background: editFieldBg,
|
||||
borderRadius: '2px',
|
||||
padding: '1px 2px'
|
||||
},
|
||||
'&::-webkit-calendar-picker-indicator': {
|
||||
filter: calendarFilter
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
|
@ -830,12 +869,12 @@ export default function PlotHistoricalSession({
|
|||
{/* Fullscreen Modal */}
|
||||
<Modal isOpen={isFullscreen} onClose={closeFullscreen} size="full">
|
||||
<ModalOverlay bg="blackAlpha.800" />
|
||||
<ModalContent bg={useColorModeValue('white', 'gray.700')} m={0} borderRadius={0} h="100vh">
|
||||
<ModalContent bg={modalBg} m={0} borderRadius={0} h="100vh">
|
||||
<ModalHeader>
|
||||
<HStack>
|
||||
<Text>📈 {session.name} - Fullscreen Mode</Text>
|
||||
<Spacer />
|
||||
<Text fontSize="sm" color={useColorModeValue('gray.600', 'gray.300')}>
|
||||
<Text fontSize="sm" color={modalTextColor}>
|
||||
Zoom: Drag to select area | Pan: Shift + Drag | Double-click to reset
|
||||
</Text>
|
||||
</HStack>
|
||||
|
@ -904,7 +943,7 @@ export default function PlotHistoricalSession({
|
|||
</Button>
|
||||
|
||||
{/* Time range info */}
|
||||
<Box px={3} py={1} bg={useColorModeValue('blue.50', 'blue.900')} borderRadius="md" fontSize="xs">
|
||||
<Box px={3} py={1} bg={highlightBg} borderRadius="md" fontSize="xs">
|
||||
<HStack spacing={4}>
|
||||
<Text><strong>Range:</strong> {timeRangeSeconds}s</Text>
|
||||
<Text><strong>From:</strong> {formatCentralTimeInfo().start}</Text>
|
||||
|
|
|
@ -203,6 +203,34 @@ export default function TimePointSelector({
|
|||
'& .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')
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
|
Loading…
Reference in New Issue