Enhance Filter Functionality Across Dashboard Components
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Updated FilterInput, FilterSidebar, ObjectTable, SimpleDateTimePropertyFilter, and SimplePropertyFilter components to support new filter and masterFilter props, improving data filtering capabilities. - Introduced ObjectTableFilterContext for better state management of filter values across components, enhancing the overall user experience. - Refactored getModelPropertyValues function in ApiServerContext to accept filter parameters, allowing for more dynamic data fetching based on user-defined filters. - Improved loading and visibility handling in SimpleDateTimePropertyFilter and SimplePropertyFilter to optimize performance during data retrieval.
This commit is contained in:
parent
f447ec287a
commit
0ae4f1fae0
@ -442,7 +442,9 @@ const FilterInput = ({
|
|||||||
onBlur,
|
onBlur,
|
||||||
onPressEnter,
|
onPressEnter,
|
||||||
size,
|
size,
|
||||||
propertyFilter = null
|
propertyFilter = null,
|
||||||
|
filter = {},
|
||||||
|
masterFilter = {}
|
||||||
}) => {
|
}) => {
|
||||||
const { getPrefixCls, direction } = useContext(ConfigProvider.ConfigContext)
|
const { getPrefixCls, direction } = useContext(ConfigProvider.ConfigContext)
|
||||||
const prefixCls = getPrefixCls('input')
|
const prefixCls = getPrefixCls('input')
|
||||||
@ -895,6 +897,10 @@ const FilterInput = ({
|
|||||||
propertyName={propertyFilter.propertyName}
|
propertyName={propertyFilter.propertyName}
|
||||||
value={propertyFilterValue}
|
value={propertyFilterValue}
|
||||||
onChange={handlePropertyFilterChange}
|
onChange={handlePropertyFilterChange}
|
||||||
|
filter={filter}
|
||||||
|
masterFilter={masterFilter}
|
||||||
|
visible={true}
|
||||||
|
useTableFilter={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -1146,7 +1152,8 @@ const FilterInput = ({
|
|||||||
{propertyFilterEnabled && (
|
{propertyFilterEnabled && (
|
||||||
<Popover
|
<Popover
|
||||||
open={focused}
|
open={focused}
|
||||||
content={propertyFilterContent}
|
destroyOnHidden={true}
|
||||||
|
content={focused ? propertyFilterContent : null}
|
||||||
placement='bottomLeft'
|
placement='bottomLeft'
|
||||||
arrow={false}
|
arrow={false}
|
||||||
trigger={[]}
|
trigger={[]}
|
||||||
@ -1191,7 +1198,9 @@ FilterInput.propTypes = {
|
|||||||
propertyFilter: PropTypes.shape({
|
propertyFilter: PropTypes.shape({
|
||||||
modelType: PropTypes.string.isRequired,
|
modelType: PropTypes.string.isRequired,
|
||||||
propertyName: PropTypes.string.isRequired
|
propertyName: PropTypes.string.isRequired
|
||||||
})
|
}),
|
||||||
|
filter: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FilterInput
|
export default FilterInput
|
||||||
|
|||||||
@ -215,6 +215,8 @@ const FilterSidebar = ({
|
|||||||
modelType: type,
|
modelType: type,
|
||||||
propertyName: row.field
|
propertyName: row.field
|
||||||
}}
|
}}
|
||||||
|
filter={localFilter}
|
||||||
|
masterFilter={masterFilter}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<CloseOutlined />}
|
icon={<CloseOutlined />}
|
||||||
|
|||||||
@ -56,6 +56,7 @@ import {
|
|||||||
} from '../context/TableStateContext'
|
} from '../context/TableStateContext'
|
||||||
import { hasActionPermission } from '../../../database/permissions'
|
import { hasActionPermission } from '../../../database/permissions'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
|
import { ObjectTableFilterContext } from './ObjectTableFilterContext'
|
||||||
|
|
||||||
const logger = loglevel.getLogger('DasboardTable')
|
const logger = loglevel.getLogger('DasboardTable')
|
||||||
logger.setLevel(config.logLevel)
|
logger.setLevel(config.logLevel)
|
||||||
@ -98,7 +99,9 @@ const ColumnFilterDropdown = ({
|
|||||||
visible,
|
visible,
|
||||||
propertyName,
|
propertyName,
|
||||||
propertyLabel,
|
propertyLabel,
|
||||||
modelType
|
modelType,
|
||||||
|
filter = {},
|
||||||
|
masterFilter = {}
|
||||||
}) => {
|
}) => {
|
||||||
const [expression, setExpression] = useState('')
|
const [expression, setExpression] = useState('')
|
||||||
const [draft, setDraft] = useState(selectedKeys || [])
|
const [draft, setDraft] = useState(selectedKeys || [])
|
||||||
@ -164,6 +167,9 @@ const ColumnFilterDropdown = ({
|
|||||||
propertyName={propertyName}
|
propertyName={propertyName}
|
||||||
value={draft}
|
value={draft}
|
||||||
onChange={handleDraftChange}
|
onChange={handleDraftChange}
|
||||||
|
filter={filter}
|
||||||
|
masterFilter={masterFilter}
|
||||||
|
visible={visible}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ScrollBox>
|
</ScrollBox>
|
||||||
@ -181,7 +187,9 @@ ColumnFilterDropdown.propTypes = {
|
|||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
propertyName: PropTypes.string,
|
propertyName: PropTypes.string,
|
||||||
propertyLabel: PropTypes.string,
|
propertyLabel: PropTypes.string,
|
||||||
modelType: PropTypes.string
|
modelType: PropTypes.string,
|
||||||
|
filter: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object
|
||||||
}
|
}
|
||||||
|
|
||||||
const RowForm = ({ record, isEditing, onRegister, children }) => {
|
const RowForm = ({ record, isEditing, onRegister, children }) => {
|
||||||
@ -867,6 +875,14 @@ const ObjectTable = forwardRef(
|
|||||||
return { ...active, ...masterFilter }
|
return { ...active, ...masterFilter }
|
||||||
}, [sidebarFilter, masterFilter])
|
}, [sidebarFilter, masterFilter])
|
||||||
|
|
||||||
|
const tableFilterContextValue = useMemo(
|
||||||
|
() => ({
|
||||||
|
filter: getActiveFilterValues(sidebarFilter),
|
||||||
|
masterFilter
|
||||||
|
}),
|
||||||
|
[sidebarFilter, masterFilter]
|
||||||
|
)
|
||||||
|
|
||||||
newEventHandlerRef.current = newEventHandler
|
newEventHandlerRef.current = newEventHandler
|
||||||
subscriptionFilterRef.current = subscriptionFilter
|
subscriptionFilterRef.current = subscriptionFilter
|
||||||
subscribeToObjectTypeUpdatesFnRef.current = subscribeToObjectTypeUpdates
|
subscribeToObjectTypeUpdatesFnRef.current = subscribeToObjectTypeUpdates
|
||||||
@ -1112,6 +1128,8 @@ const ObjectTable = forwardRef(
|
|||||||
propertyName={propertyName}
|
propertyName={propertyName}
|
||||||
propertyLabel={propertyLabel}
|
propertyLabel={propertyLabel}
|
||||||
modelType={type}
|
modelType={type}
|
||||||
|
filter={sidebarFilter}
|
||||||
|
masterFilter={masterFilter}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1583,7 +1601,11 @@ const ObjectTable = forwardRef(
|
|||||||
</Flex>
|
</Flex>
|
||||||
)
|
)
|
||||||
|
|
||||||
return tableContent
|
return (
|
||||||
|
<ObjectTableFilterContext.Provider value={tableFilterContextValue}>
|
||||||
|
{tableContent}
|
||||||
|
</ObjectTableFilterContext.Provider>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,3 @@
|
|||||||
|
import { createContext } from 'react'
|
||||||
|
|
||||||
|
export const ObjectTableFilterContext = createContext(null)
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useContext, useMemo, useCallback } from 'react'
|
import { useState, useEffect, useContext, useMemo, useCallback, useRef } from 'react'
|
||||||
import { Spin, Tree, Flex, Checkbox, Typography } from 'antd'
|
import { Spin, Tree, Flex, Checkbox, Typography } from 'antd'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
@ -369,31 +369,94 @@ const expandKeysForChecked = (keys, nodeByKey) => {
|
|||||||
return [...checked]
|
return [...checked]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EMPTY_OBJECT = {}
|
||||||
|
|
||||||
|
const stableStringify = (value) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return `[${value.map(stableStringify).join(',')}]`
|
||||||
|
}
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
return `{${Object.keys(value)
|
||||||
|
.sort()
|
||||||
|
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
|
||||||
|
.join(',')}}`
|
||||||
|
}
|
||||||
|
return JSON.stringify(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilterHash = (filter, masterFilter) =>
|
||||||
|
`${stableStringify(filter || {})}:${stableStringify(masterFilter || {})}`
|
||||||
|
|
||||||
const SimpleDateTimePropertyFilter = ({
|
const SimpleDateTimePropertyFilter = ({
|
||||||
modelType,
|
modelType,
|
||||||
propertyName,
|
propertyName,
|
||||||
value = [],
|
value = [],
|
||||||
onChange,
|
onChange,
|
||||||
search = ''
|
search = '',
|
||||||
|
filter = EMPTY_OBJECT,
|
||||||
|
masterFilter = EMPTY_OBJECT,
|
||||||
|
visible = true
|
||||||
}) => {
|
}) => {
|
||||||
const { getModelPropertyValues } = useContext(ApiServerContext)
|
const { getModelPropertyValues } = useContext(ApiServerContext)
|
||||||
|
const getModelPropertyValuesRef = useRef(getModelPropertyValues)
|
||||||
|
getModelPropertyValuesRef.current = getModelPropertyValues
|
||||||
|
|
||||||
const [dates, setDates] = useState([])
|
const [dates, setDates] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [localChecked, setLocalChecked] = useState(null)
|
const [localChecked, setLocalChecked] = useState(null)
|
||||||
|
|
||||||
|
const filterForValues = useMemo(() => {
|
||||||
|
const next = { ...(filter || {}) }
|
||||||
|
if (propertyName) delete next[propertyName]
|
||||||
|
return next
|
||||||
|
}, [filter, propertyName])
|
||||||
|
|
||||||
|
const masterFilterForValues = masterFilter || EMPTY_OBJECT
|
||||||
|
|
||||||
|
const filterHash = useMemo(
|
||||||
|
() => getFilterHash(filterForValues, masterFilterForValues),
|
||||||
|
[filterForValues, masterFilterForValues]
|
||||||
|
)
|
||||||
|
const loadedFilterHashRef = useRef(null)
|
||||||
|
const filterHashRef = useRef(filterHash)
|
||||||
|
const filterForValuesRef = useRef(filterForValues)
|
||||||
|
const masterFilterForValuesRef = useRef(masterFilterForValues)
|
||||||
|
const wasVisibleRef = useRef(false)
|
||||||
|
filterHashRef.current = filterHash
|
||||||
|
filterForValuesRef.current = filterForValues
|
||||||
|
masterFilterForValuesRef.current = masterFilterForValues
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const becameVisible = visible && !wasVisibleRef.current
|
||||||
|
wasVisibleRef.current = visible
|
||||||
|
if (!visible) return
|
||||||
|
if (!becameVisible && loadedFilterHashRef.current != null) return
|
||||||
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
const loadOptions = async () => {
|
const loadOptions = async () => {
|
||||||
if (!modelType || !propertyName) return
|
if (!modelType || !propertyName) return
|
||||||
|
|
||||||
|
const currentHash = filterHashRef.current
|
||||||
|
if (loadedFilterHashRef.current === currentHash) return
|
||||||
|
|
||||||
|
setDates([])
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const values = await getModelPropertyValues(modelType, propertyName)
|
const values = await getModelPropertyValuesRef.current(
|
||||||
|
modelType,
|
||||||
|
propertyName,
|
||||||
|
{
|
||||||
|
filter: filterForValuesRef.current,
|
||||||
|
masterFilter: masterFilterForValuesRef.current
|
||||||
|
}
|
||||||
|
)
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
const parsed = (values || [])
|
const parsed = (values || [])
|
||||||
.map(parseDateValue)
|
.map(parseDateValue)
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.sort((a, b) => a.getTime() - b.getTime())
|
.sort((a, b) => a.getTime() - b.getTime())
|
||||||
|
loadedFilterHashRef.current = currentHash
|
||||||
setDates(parsed)
|
setDates(parsed)
|
||||||
} finally {
|
} finally {
|
||||||
if (!cancelled) setLoading(false)
|
if (!cancelled) setLoading(false)
|
||||||
@ -404,7 +467,7 @@ const SimpleDateTimePropertyFilter = ({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [modelType, propertyName, getModelPropertyValues])
|
}, [visible, modelType, propertyName])
|
||||||
|
|
||||||
const treeData = useMemo(
|
const treeData = useMemo(
|
||||||
() => buildTreeData(buildDateHierarchy(dates)),
|
() => buildTreeData(buildDateHierarchy(dates)),
|
||||||
@ -569,7 +632,10 @@ SimpleDateTimePropertyFilter.propTypes = {
|
|||||||
propertyName: PropTypes.string,
|
propertyName: PropTypes.string,
|
||||||
value: PropTypes.array,
|
value: PropTypes.array,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
search: PropTypes.string
|
search: PropTypes.string,
|
||||||
|
filter: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object,
|
||||||
|
visible: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SimpleDateTimePropertyFilter
|
export default SimpleDateTimePropertyFilter
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { useState, useEffect, useContext, useMemo, useRef } 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 { ObjectTableFilterContext } from './ObjectTableFilterContext'
|
||||||
import {
|
import {
|
||||||
getModelByName,
|
getModelByName,
|
||||||
getModelProperties
|
getModelProperties
|
||||||
@ -50,27 +51,91 @@ const matchOptions = (options, selected) => {
|
|||||||
return options.filter((option) => selectedKeys.has(getOptionKey(option)))
|
return options.filter((option) => selectedKeys.has(getOptionKey(option)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const stableStringify = (value) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return `[${value.map(stableStringify).join(',')}]`
|
||||||
|
}
|
||||||
|
if (value && typeof value === 'object') {
|
||||||
|
return `{${Object.keys(value)
|
||||||
|
.sort()
|
||||||
|
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
|
||||||
|
.join(',')}}`
|
||||||
|
}
|
||||||
|
return JSON.stringify(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getFilterHash = (filter, masterFilter) =>
|
||||||
|
`${stableStringify(filter || {})}:${stableStringify(masterFilter || {})}`
|
||||||
|
|
||||||
|
const omitUndefinedValues = (obj) =>
|
||||||
|
Object.fromEntries(
|
||||||
|
Object.entries(obj || {}).filter(
|
||||||
|
([, value]) => value !== undefined && value !== ''
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Survive table reloads / popover remounts without refetching the same property.
|
// Survive table reloads / popover remounts without refetching the same property.
|
||||||
const optionsCache = new Map()
|
const optionsCache = new Map()
|
||||||
|
const lastLoadedFilterHashByProperty = new Map()
|
||||||
|
|
||||||
|
const EMPTY_OBJECT = {}
|
||||||
|
|
||||||
const SimplePropertyFilter = ({
|
const SimplePropertyFilter = ({
|
||||||
modelType,
|
modelType,
|
||||||
propertyName,
|
propertyName,
|
||||||
value = [],
|
value = [],
|
||||||
onChange,
|
onChange,
|
||||||
search = ''
|
search = '',
|
||||||
|
filter = EMPTY_OBJECT,
|
||||||
|
masterFilter = EMPTY_OBJECT,
|
||||||
|
visible = true,
|
||||||
|
useTableFilter = true
|
||||||
}) => {
|
}) => {
|
||||||
const { getModelPropertyValues } = useContext(ApiServerContext)
|
const { getModelPropertyValues } = useContext(ApiServerContext)
|
||||||
|
const tableFilterContext = useContext(ObjectTableFilterContext)
|
||||||
const getModelPropertyValuesRef = useRef(getModelPropertyValues)
|
const getModelPropertyValuesRef = useRef(getModelPropertyValues)
|
||||||
getModelPropertyValuesRef.current = getModelPropertyValues
|
getModelPropertyValuesRef.current = getModelPropertyValues
|
||||||
|
|
||||||
|
const resolvedFilter = useTableFilter
|
||||||
|
? (tableFilterContext?.filter ?? filter)
|
||||||
|
: filter
|
||||||
|
const resolvedMasterFilter = useTableFilter
|
||||||
|
? (tableFilterContext?.masterFilter ?? masterFilter)
|
||||||
|
: masterFilter || tableFilterContext?.masterFilter || EMPTY_OBJECT
|
||||||
|
|
||||||
|
const filterForValues = useMemo(() => {
|
||||||
|
const next = omitUndefinedValues(resolvedFilter)
|
||||||
|
if (propertyName) delete next[propertyName]
|
||||||
|
return next
|
||||||
|
}, [resolvedFilter, propertyName])
|
||||||
|
|
||||||
|
const masterFilterForValues = useMemo(
|
||||||
|
() => omitUndefinedValues(resolvedMasterFilter),
|
||||||
|
[resolvedMasterFilter]
|
||||||
|
)
|
||||||
|
|
||||||
|
const filterHash = useMemo(
|
||||||
|
() => getFilterHash(filterForValues, masterFilterForValues),
|
||||||
|
[filterForValues, masterFilterForValues]
|
||||||
|
)
|
||||||
|
const loadedFilterHashRef = useRef(null)
|
||||||
|
const filterHashRef = useRef(filterHash)
|
||||||
|
const filterForValuesRef = useRef(filterForValues)
|
||||||
|
const masterFilterForValuesRef = useRef(masterFilterForValues)
|
||||||
|
const wasVisibleRef = useRef(false)
|
||||||
|
filterHashRef.current = filterHash
|
||||||
|
filterForValuesRef.current = filterForValues
|
||||||
|
masterFilterForValuesRef.current = masterFilterForValues
|
||||||
|
|
||||||
const cacheKey =
|
const cacheKey =
|
||||||
modelType && propertyName ? `${modelType}:${propertyName}` : null
|
modelType && propertyName
|
||||||
|
? `${modelType}:${propertyName}:${filterHash}`
|
||||||
|
: null
|
||||||
const [options, setOptions] = useState(
|
const [options, setOptions] = useState(
|
||||||
() => (cacheKey && optionsCache.get(cacheKey)) || []
|
() => (cacheKey && optionsCache.get(cacheKey)) || []
|
||||||
)
|
)
|
||||||
const [loading, setLoading] = useState(
|
const [loading, setLoading] = useState(
|
||||||
() => !(cacheKey && optionsCache.has(cacheKey))
|
() => visible && !(cacheKey && optionsCache.has(cacheKey))
|
||||||
)
|
)
|
||||||
const [localChecked, setLocalChecked] = useState(null)
|
const [localChecked, setLocalChecked] = useState(null)
|
||||||
|
|
||||||
@ -81,6 +146,11 @@ const SimplePropertyFilter = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const becameVisible = visible && !wasVisibleRef.current
|
||||||
|
wasVisibleRef.current = visible
|
||||||
|
if (!visible) return
|
||||||
|
if (!becameVisible && loadedFilterHashRef.current != null) return
|
||||||
|
|
||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
const loadOptions = async () => {
|
const loadOptions = async () => {
|
||||||
@ -89,25 +159,40 @@ const SimplePropertyFilter = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = `${modelType}:${propertyName}`
|
const currentHash = filterHashRef.current
|
||||||
const cached = optionsCache.get(key)
|
const propertyKey = `${modelType}:${propertyName}`
|
||||||
if (cached) {
|
const lastHash =
|
||||||
setOptions(cached)
|
loadedFilterHashRef.current ??
|
||||||
setLoading(false)
|
lastLoadedFilterHashByProperty.get(propertyKey)
|
||||||
return
|
|
||||||
|
if (lastHash === currentHash) {
|
||||||
|
loadedFilterHashRef.current = currentHash
|
||||||
|
const cached = optionsCache.get(`${propertyKey}:${currentHash}`)
|
||||||
|
if (cached) {
|
||||||
|
setOptions(cached)
|
||||||
|
setLoading(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOptions([])
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const values = await getModelPropertyValuesRef.current(
|
const values = await getModelPropertyValuesRef.current(
|
||||||
modelType,
|
modelType,
|
||||||
propertyName
|
propertyName,
|
||||||
|
{
|
||||||
|
filter: filterForValuesRef.current,
|
||||||
|
masterFilter: masterFilterForValuesRef.current
|
||||||
|
}
|
||||||
)
|
)
|
||||||
if (cancelled) return
|
if (cancelled) return
|
||||||
const unique = [
|
const unique = [
|
||||||
...new Set((values || []).filter((v) => v != null && v !== ''))
|
...new Set((values || []).filter((v) => v != null && v !== ''))
|
||||||
]
|
]
|
||||||
optionsCache.set(key, unique)
|
optionsCache.set(`${propertyKey}:${currentHash}`, unique)
|
||||||
|
lastLoadedFilterHashByProperty.set(propertyKey, currentHash)
|
||||||
|
loadedFilterHashRef.current = currentHash
|
||||||
setOptions(unique)
|
setOptions(unique)
|
||||||
} finally {
|
} finally {
|
||||||
if (!cancelled) setLoading(false)
|
if (!cancelled) setLoading(false)
|
||||||
@ -118,9 +203,8 @@ const SimplePropertyFilter = ({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
// Intentionally omit getModelPropertyValues — context recreates it on every
|
// Reload only when this filter becomes visible, then compare filterHash.
|
||||||
// provider render (e.g. table fetchLoading), which would refetch endlessly.
|
}, [visible, modelType, propertyName, property])
|
||||||
}, [modelType, propertyName, property])
|
|
||||||
|
|
||||||
const valueKey = useMemo(
|
const valueKey = useMemo(
|
||||||
() => JSON.stringify((value || []).map(getOptionKey)),
|
() => JSON.stringify((value || []).map(getOptionKey)),
|
||||||
@ -179,6 +263,9 @@ const SimplePropertyFilter = ({
|
|||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
search={search}
|
search={search}
|
||||||
|
filter={resolvedFilter}
|
||||||
|
masterFilter={resolvedMasterFilter}
|
||||||
|
visible={visible}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -225,7 +312,11 @@ SimplePropertyFilter.propTypes = {
|
|||||||
propertyName: PropTypes.string,
|
propertyName: PropTypes.string,
|
||||||
value: PropTypes.array,
|
value: PropTypes.array,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
search: PropTypes.string
|
search: PropTypes.string,
|
||||||
|
filter: PropTypes.object,
|
||||||
|
masterFilter: PropTypes.object,
|
||||||
|
visible: PropTypes.bool,
|
||||||
|
useTableFilter: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default SimplePropertyFilter
|
export default SimplePropertyFilter
|
||||||
|
|||||||
@ -1874,17 +1874,29 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getModelPropertyValues = async (objectType, property) => {
|
const getModelPropertyValues = async (objectType, property, params = {}) => {
|
||||||
|
const { filter = {}, masterFilter = {} } = params
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
'Fetching property values for model type:',
|
'Fetching property values for model type:',
|
||||||
objectType,
|
objectType,
|
||||||
property
|
property,
|
||||||
|
{ filter, masterFilter }
|
||||||
)
|
)
|
||||||
try {
|
try {
|
||||||
const response = await axios.get(
|
const response = await axios.get(
|
||||||
`${config.backendUrl}/${getObjectEndpoint(objectType)}/values`,
|
`${config.backendUrl}/${getObjectEndpoint(objectType)}/values`,
|
||||||
{
|
{
|
||||||
params: { property },
|
params: {
|
||||||
|
property,
|
||||||
|
...Object.keys(filter).reduce((acc, key) => {
|
||||||
|
acc[key] = Array.isArray(filter[key])
|
||||||
|
? filter[key].join(',')
|
||||||
|
: filter[key]
|
||||||
|
return acc
|
||||||
|
}, {}),
|
||||||
|
masterFilter: JSON.stringify(masterFilter)
|
||||||
|
},
|
||||||
headers: {
|
headers: {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
Authorization: `Bearer ${token}`
|
Authorization: `Bearer ${token}`
|
||||||
@ -1900,7 +1912,7 @@ const ApiServerProvider = ({ children }) => {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
showError(err, () => {
|
showError(err, () => {
|
||||||
getModelPropertyValues(objectType, property)
|
getModelPropertyValues(objectType, property, params)
|
||||||
})
|
})
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user