feat: Enhance Checkbox and Switch widgets for improved layout and error handling

This commit is contained in:
Miguel 2025-08-27 14:49:43 +02:00
parent 42cd1743e8
commit 61d61d27d1
2 changed files with 569 additions and 45 deletions

View File

@ -18447,8 +18447,514 @@
"read_time_avg": 0.03063199520111084,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:45:54.611177",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.021202087402344,
"points_saved": 20,
"points_rate": 1.9957685540681798,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02488781213760376,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:04.633274",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.02209734916687,
"points_saved": 20,
"points_rate": 1.995590274491056,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.025893700122833253,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:14.654865",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.02159070968628,
"points_saved": 20,
"points_rate": 1.9956911611516102,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.0258081316947937,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:24.677731",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.022866249084473,
"points_saved": 20,
"points_rate": 1.9954371836326636,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02811945676803589,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:34.703577",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.025846242904663,
"points_saved": 20,
"points_rate": 1.9948440775414935,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02674727439880371,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:44.727852",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.6% CPU",
"details": {
"duration": 10.023722410202026,
"points_saved": 20,
"points_rate": 1.9952667463779958,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.6,
"cpu_max": 0.6,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.026058709621429442,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:46:54.749475",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.0% CPU",
"details": {
"duration": 10.022175073623657,
"points_saved": 20,
"points_rate": 1.9955747981928558,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.0,
"cpu_max": 0.0,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.0281419038772583,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:04.770778",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.021302938461304,
"points_saved": 20,
"points_rate": 1.995748469317389,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02705467939376831,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:14.795986",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.025207996368408,
"points_saved": 20,
"points_rate": 1.9949710776319973,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.03037310838699341,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:24.818898",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.022911787033081,
"points_saved": 20,
"points_rate": 1.9954281175929887,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.028278648853302002,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:34.842175",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.0% CPU",
"details": {
"duration": 10.023277759552002,
"points_saved": 20,
"points_rate": 1.9953552600036812,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.0,
"cpu_max": 0.0,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.026952099800109864,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:44.864412",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.021728754043579,
"points_saved": 20,
"points_rate": 1.9956636714928426,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02594623565673828,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:47:54.886695",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.022790908813477,
"points_saved": 20,
"points_rate": 1.9954521831252738,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02918875217437744,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:04.909449",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.5% CPU",
"details": {
"duration": 10.022753715515137,
"points_saved": 20,
"points_rate": 1.9954595880212214,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.5,
"cpu_max": 0.5,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.025779426097869873,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:14.931798",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.021842241287231,
"points_saved": 20,
"points_rate": 1.9956410726169191,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02666689157485962,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:24.962713",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.03142237663269,
"points_saved": 20,
"points_rate": 1.9937352101321373,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02565774917602539,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:34.984508",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.5% CPU",
"details": {
"duration": 10.021795272827148,
"points_saved": 20,
"points_rate": 1.9956504254509682,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.5,
"cpu_max": 0.5,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.02610586881637573,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:45.011512",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 21 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.027003526687622,
"points_saved": 21,
"points_rate": 2.0943445311559854,
"variables_saved": 84,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.026610397157214936,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:48:55.039493",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.027981042861938,
"points_saved": 20,
"points_rate": 1.9944194065101757,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.024284172058105468,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:49:05.062227",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.022226572036743,
"points_saved": 20,
"points_rate": 1.9955645440906797,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.029187917709350586,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:49:15.084325",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.3% CPU",
"details": {
"duration": 10.022605180740356,
"points_saved": 20,
"points_rate": 1.9954891606857277,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.3,
"cpu_max": 0.3,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.0247170090675354,
"csv_write_time_avg": 0.0
}
},
{
"timestamp": "2025-08-27T14:49:25.108902",
"level": "info",
"event_type": "performance_report",
"message": "Performance report: 20 points saved, 0 lost, 0.2% CPU",
"details": {
"duration": 10.024576902389526,
"points_saved": 20,
"points_rate": 1.995096670387422,
"variables_saved": 80,
"udp_points_sent": 0,
"points_lost": 0,
"cpu_average": 0.2,
"cpu_max": 0.2,
"delay_average": 0.0,
"delay_max": 0.0,
"read_errors": 0,
"csv_errors": 0,
"udp_errors": 0,
"read_time_avg": 0.026402544975280762,
"csv_write_time_avg": 0.0
}
}
],
"last_updated": "2025-08-27T14:45:44.589975",
"total_entries": 839
"last_updated": "2025-08-27T14:49:25.108902",
"total_entries": 861
}

View File

@ -132,62 +132,79 @@ export const SelectWidget = ({ id, required, readonly, disabled, label, value, o
}
export const CheckboxWidget = ({ id, label, value, required, disabled, readonly, onChange, rawErrors = [] }) => (
<FormControl isRequired={required} isDisabled={disabled} isReadOnly={readonly} isInvalid={rawErrors.length > 0}>
<Checkbox
id={id}
isChecked={!!value}
onChange={(e) => onChange(e.target.checked)}
colorScheme="blue"
alignItems="center"
sx={{
'& .chakra-checkbox__control': {
alignSelf: 'flex-start',
mt: '2px' // Small offset to align with text baseline
},
'& .chakra-checkbox__label': {
fontSize: 'sm',
lineHeight: '1.3',
overflow: 'hidden',
textOverflow: 'ellipsis',
display: '-webkit-box',
WebkitLineClamp: 1, // Force single line
WebkitBoxOrient: 'vertical',
wordBreak: 'break-word'
}
}}
>
{label}
</Checkbox>
{rawErrors.length > 0 && (
<FormHelperText color="red.500">{rawErrors[0]}</FormHelperText>
)}
</FormControl>
)
export const SwitchWidget = ({ id, label, value, required, disabled, readonly, onChange, rawErrors = [] }) => (
<FormControl
display="flex"
alignItems="center"
isRequired={required}
isDisabled={disabled}
isReadOnly={readonly}
isInvalid={rawErrors.length > 0}
minH="40px" // Standard height to match other inputs
position="relative"
display="flex"
flexDirection="column"
justifyContent="flex-end" // Align to bottom of container
alignItems="center" // Center horizontally
>
<FormLabel
htmlFor={id}
mb="0"
mr={3}
mb={1} // Small margin between label and checkbox
fontSize="sm"
lineHeight="1.3"
lineHeight="1.2"
overflow="hidden"
textOverflow="ellipsis"
whiteSpace="nowrap" // Force single line
wordBreak="break-word"
display="flex"
alignItems="center"
maxW="calc(100% - 60px)" // Reserve space for switch (approx 50px + margin)
textAlign="center" // Center the label text
maxW="100%"
order={1} // Label first
>
{label}
</FormLabel>
<Checkbox
id={id}
isChecked={!!value}
onChange={(e) => onChange(e.target.checked)}
colorScheme="blue"
order={2} // Checkbox second
alignSelf="center" // Ensure checkbox is centered
sx={{
'& .chakra-checkbox__control': {
alignSelf: 'center'
}
}}
/>
{rawErrors.length > 0 && (
<FormHelperText color="red.500" position="absolute" top="100%" left={0} mt={1} w="100%" textAlign="center">
{rawErrors[0]}
</FormHelperText>
)}
</FormControl>
)
export const SwitchWidget = ({ id, label, value, required, disabled, readonly, onChange, rawErrors = [] }) => (
<FormControl
isRequired={required}
isDisabled={disabled}
isReadOnly={readonly}
isInvalid={rawErrors.length > 0}
minH="40px" // Standard height to match other inputs
position="relative"
display="flex"
flexDirection="column"
justifyContent="flex-end" // Align to bottom of container
alignItems="center" // Center horizontally
>
<FormLabel
htmlFor={id}
mb={1} // Small margin between label and switch
fontSize="sm"
lineHeight="1.2"
overflow="hidden"
textOverflow="ellipsis"
whiteSpace="nowrap" // Force single line
wordBreak="break-word"
textAlign="center" // Center the label text
maxW="100%"
order={1} // Label first
>
{label}
</FormLabel>
@ -196,10 +213,11 @@ export const SwitchWidget = ({ id, label, value, required, disabled, readonly, o
isChecked={!!value}
onChange={(e) => onChange(e.target.checked)}
colorScheme="blue"
flexShrink={0} // Prevent switch from shrinking
order={2} // Switch second
alignSelf="center" // Ensure switch is centered
/>
{rawErrors.length > 0 && (
<FormHelperText color="red.500" position="absolute" top="100%" left={0} mt={1}>
<FormHelperText color="red.500" position="absolute" top="100%" left={0} mt={1} w="100%" textAlign="center">
{rawErrors[0]}
</FormHelperText>
)}