Refactor SimplePropertyFilter for improved key handling and filtering logic
Some checks failed
farmcontrol/farmcontrol-ui/pipeline/head There was a failure building this commit

- Enhanced the getOptionKey function to support object types and improve key generation for options.
- Updated local state management to utilize keys for checked values, ensuring consistent behavior during filtering.
- Refactored handleChange and emitChange functions to work with keys, improving clarity and maintainability.
- Adjusted Checkbox component to use generated keys for value handling, enhancing the filtering experience.
This commit is contained in:
Tom Butcher 2026-08-10 00:56:55 +01:00
parent 0b110cdfff
commit 38e89e45c7

View File

@ -2,13 +2,21 @@ import { useState, useEffect, useContext, useMemo } from 'react'
import { Flex, Checkbox, Spin, Typography } from 'antd' import { Flex, Checkbox, Spin, Typography } from 'antd'
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext' import { ApiServerContext } from '../context/ApiServerContext'
import { getModelProperties } from '../../../database/ObjectModels' import {
getModelByName,
getModelProperties
} from '../../../database/ObjectModels'
import ObjectProperty from './ObjectProperty' import ObjectProperty from './ObjectProperty'
import { LoadingOutlined } from '@ant-design/icons' import { LoadingOutlined } from '@ant-design/icons'
const { Text } = Typography const { Text } = Typography
const getOptionKey = (option) => { const getOptionKey = (option) => {
if (option && typeof option === 'object') { if (option && typeof option === 'object') {
if (option.objectType) {
const { prefix } = getModelByName(option.objectType)
if (option._reference != null) return `${prefix}:${option._reference}`
if (option._id != null) return `${prefix}:${option._id}`
}
return String(option._id ?? option.type ?? JSON.stringify(option)) return String(option._id ?? option.type ?? JSON.stringify(option))
} }
return String(option) return String(option)
@ -86,9 +94,12 @@ const SimplePropertyFilter = ({
if (options.length === 0) return if (options.length === 0) return
if (value?.length > 0) { if (value?.length > 0) {
const matched = matchOptions(options, value) const matched = matchOptions(options, value)
setLocalChecked(matched.length > 0 ? matched : [...value]) const matchedKeys = matched.map(getOptionKey)
setLocalChecked(
matchedKeys.length > 0 ? matchedKeys : value.map(getOptionKey)
)
} else { } else {
setLocalChecked(options) setLocalChecked(options.map(getOptionKey))
} }
// valueKey captures value contents; value is read for matching // valueKey captures value contents; value is read for matching
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@ -102,43 +113,44 @@ const SimplePropertyFilter = ({
) )
}, [options, search]) }, [options, search])
const checkedValues = localChecked ?? options const optionKeys = useMemo(() => options.map(getOptionKey), [options])
const checkedKeys = localChecked ?? optionKeys
const emitChange = (next) => { const emitChange = (nextKeys) => {
setLocalChecked(next) setLocalChecked(nextKeys)
// All selected (or empty options) means no filter applied // All selected (or empty options) means no filter applied
if (options.length > 0 && next.length === options.length) { if (optionKeys.length > 0 && nextKeys.length === optionKeys.length) {
onChange?.([]) onChange?.([])
} else { } else {
onChange?.(next) onChange?.(nextKeys)
} }
} }
const handleChange = (visibleChecked) => { const handleChange = (visibleCheckedKeys) => {
const hiddenSelected = checkedValues.filter( const filteredKeys = new Set(filteredOptions.map(getOptionKey))
(checked) => const hiddenSelected = checkedKeys.filter((key) => !filteredKeys.has(key))
!filteredOptions.some( emitChange([...hiddenSelected, ...visibleCheckedKeys])
(option) => getOptionKey(option) === getOptionKey(checked)
)
)
emitChange([...hiddenSelected, ...visibleChecked])
} }
return ( return (
<Spin spinning={loading} indicator={<LoadingOutlined spin />}> <Spin spinning={loading} indicator={<LoadingOutlined spin />}>
<Checkbox.Group <Checkbox.Group
value={checkedValues} value={checkedKeys}
onChange={handleChange} onChange={handleChange}
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<Flex vertical gap={16} style={{ minWidth: 0 }}> <Flex vertical gap={16} style={{ minWidth: 0 }}>
{filteredOptions.map((option) => ( {filteredOptions.map((option) => (
<Flex gap={14} key={getOptionKey(option)} align='center'> <Flex gap={14} key={getOptionKey(option)} align='center'>
<Checkbox value={option} style={{ minWidth: 0 }}></Checkbox> <Checkbox
value={getOptionKey(option)}
style={{ minWidth: 0 }}
></Checkbox>
<div style={{ minWidth: 0 }}> <div style={{ minWidth: 0 }}>
{property ? ( {property ? (
<ObjectProperty <ObjectProperty
{...property} {...property}
objectType={option?.objectType ?? property.objectType}
modelType={modelType} modelType={modelType}
value={getDisplayValue(option, property)} value={getDisplayValue(option, property)}
inTable={true} inTable={true}