Add SimpleDateTimePropertyFilter component for enhanced date and time filtering
- Introduced SimpleDateTimePropertyFilter component to handle filtering of date and time properties in the dashboard. - Updated SimplePropertyFilter to integrate the new component, allowing for specialized handling of dateTime and date types. - Enhanced filtering logic to improve user experience when dealing with date and time selections.
This commit is contained in:
parent
38e89e45c7
commit
9f4e6869a5
527
src/components/Dashboard/common/SimpleDateTimePropertyFilter.jsx
Normal file
527
src/components/Dashboard/common/SimpleDateTimePropertyFilter.jsx
Normal file
@ -0,0 +1,527 @@
|
|||||||
|
import { useState, useEffect, useContext, useMemo, useCallback } from 'react'
|
||||||
|
import { Spin, Tree } from 'antd'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { ApiServerContext } from '../context/ApiServerContext'
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
const MONTH_NAMES = Array.from({ length: 12 }, (_, i) =>
|
||||||
|
dayjs().month(i).format('MMMM')
|
||||||
|
)
|
||||||
|
|
||||||
|
const pad2 = (n) => String(n).padStart(2, '0')
|
||||||
|
|
||||||
|
const nodeKey = {
|
||||||
|
year: (y) => `${y}`,
|
||||||
|
month: (y, m) => `${y}-${pad2(m)}`,
|
||||||
|
day: (y, m, d) => `${y}-${pad2(m)}-${pad2(d)}`,
|
||||||
|
hour: (y, m, d, h) => `${y}-${pad2(m)}-${pad2(d)}T${pad2(h)}`,
|
||||||
|
minute: (y, m, d, h, min) =>
|
||||||
|
`${y}-${pad2(m)}-${pad2(d)}T${pad2(h)}:${pad2(min)}`,
|
||||||
|
second: (y, m, d, h, min, s) =>
|
||||||
|
`${y}-${pad2(m)}-${pad2(d)}T${pad2(h)}:${pad2(min)}:${pad2(s)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastDayOfMonth = (year, month) => new Date(year, month, 0).getDate()
|
||||||
|
|
||||||
|
const toYearExpression = (year) => `1 1 ${year}..31 12 ${year}`
|
||||||
|
|
||||||
|
const toMonthExpression = (year, month) => {
|
||||||
|
const last = lastDayOfMonth(year, month)
|
||||||
|
return `1 ${month} ${year}..${last} ${month} ${year}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const toDayExpression = (year, month, day) => `${day} ${month} ${year}`
|
||||||
|
|
||||||
|
const toHourExpression = (year, month, day, hour) =>
|
||||||
|
`${day} ${month} ${year} ${hour}`
|
||||||
|
|
||||||
|
const toMinuteExpression = (year, month, day, hour, minute) =>
|
||||||
|
`${day} ${month} ${year} ${hour} ${minute}`
|
||||||
|
|
||||||
|
const toSecondExpression = (year, month, day, hour, minute, second) =>
|
||||||
|
`${day} ${month} ${year} ${hour} ${minute} ${second}`
|
||||||
|
|
||||||
|
const parseDateValue = (value) => {
|
||||||
|
if (value == null || value === '') return null
|
||||||
|
const date = value instanceof Date ? value : new Date(value)
|
||||||
|
return Number.isNaN(date.getTime()) ? null : date
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasNonMidnightTime = (date) =>
|
||||||
|
date.getHours() !== 0 ||
|
||||||
|
date.getMinutes() !== 0 ||
|
||||||
|
date.getSeconds() !== 0 ||
|
||||||
|
date.getMilliseconds() !== 0
|
||||||
|
|
||||||
|
/** Nested map: year → month → day → hour → minute → Set(second) */
|
||||||
|
const buildDateHierarchy = (dates) => {
|
||||||
|
const root = new Map()
|
||||||
|
|
||||||
|
for (const date of dates) {
|
||||||
|
const y = date.getFullYear()
|
||||||
|
const m = date.getMonth() + 1
|
||||||
|
const d = date.getDate()
|
||||||
|
const h = date.getHours()
|
||||||
|
const min = date.getMinutes()
|
||||||
|
const s = date.getSeconds()
|
||||||
|
|
||||||
|
if (!root.has(y)) root.set(y, new Map())
|
||||||
|
const months = root.get(y)
|
||||||
|
if (!months.has(m)) months.set(m, new Map())
|
||||||
|
const days = months.get(m)
|
||||||
|
if (!days.has(d)) days.set(d, { times: new Map(), dates: [] })
|
||||||
|
const dayNode = days.get(d)
|
||||||
|
dayNode.dates.push(date)
|
||||||
|
|
||||||
|
if (!dayNode.times.has(h)) dayNode.times.set(h, new Map())
|
||||||
|
const minutes = dayNode.times.get(h)
|
||||||
|
if (!minutes.has(min)) minutes.set(min, new Set())
|
||||||
|
minutes.get(min).add(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return root
|
||||||
|
}
|
||||||
|
|
||||||
|
const dayNeedsTimeLevels = (dayNode) => {
|
||||||
|
const { dates, times } = dayNode
|
||||||
|
if (dates.some(hasNonMidnightTime)) return true
|
||||||
|
let distinct = 0
|
||||||
|
for (const minutes of times.values()) {
|
||||||
|
for (const seconds of minutes.values()) {
|
||||||
|
distinct += seconds.size
|
||||||
|
if (distinct > 1) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortNumericKeys = (map) => [...map.keys()].sort((a, b) => a - b)
|
||||||
|
|
||||||
|
const buildTreeData = (hierarchy) => {
|
||||||
|
const years = sortNumericKeys(hierarchy)
|
||||||
|
|
||||||
|
return years.map((year) => {
|
||||||
|
const monthsMap = hierarchy.get(year)
|
||||||
|
const monthNodes = sortNumericKeys(monthsMap).map((month) => {
|
||||||
|
const daysMap = monthsMap.get(month)
|
||||||
|
const dayNodes = sortNumericKeys(daysMap).map((day) => {
|
||||||
|
const dayNode = daysMap.get(day)
|
||||||
|
const includeTime = dayNeedsTimeLevels(dayNode)
|
||||||
|
let children
|
||||||
|
|
||||||
|
if (includeTime) {
|
||||||
|
children = sortNumericKeys(dayNode.times).map((hour) => {
|
||||||
|
const minutesMap = dayNode.times.get(hour)
|
||||||
|
const minuteNodes = sortNumericKeys(minutesMap).map((minute) => {
|
||||||
|
const seconds = [...minutesMap.get(minute)].sort((a, b) => a - b)
|
||||||
|
const secondNodes = seconds.map((second) => ({
|
||||||
|
key: nodeKey.second(year, month, day, hour, minute, second),
|
||||||
|
title: pad2(second),
|
||||||
|
level: 'second',
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second,
|
||||||
|
expression: toSecondExpression(
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
second
|
||||||
|
),
|
||||||
|
isLeaf: true
|
||||||
|
}))
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: nodeKey.minute(year, month, day, hour, minute),
|
||||||
|
title: pad2(minute),
|
||||||
|
level: 'minute',
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute,
|
||||||
|
expression: toMinuteExpression(
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
minute
|
||||||
|
),
|
||||||
|
children: secondNodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: nodeKey.hour(year, month, day, hour),
|
||||||
|
title: `${pad2(hour)}:00`,
|
||||||
|
level: 'hour',
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
hour,
|
||||||
|
expression: toHourExpression(year, month, day, hour),
|
||||||
|
children: minuteNodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: nodeKey.day(year, month, day),
|
||||||
|
title: String(day),
|
||||||
|
level: 'day',
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
day,
|
||||||
|
expression: toDayExpression(year, month, day),
|
||||||
|
...(children ? { children } : { isLeaf: true })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: nodeKey.month(year, month),
|
||||||
|
title: MONTH_NAMES[month - 1],
|
||||||
|
level: 'month',
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
expression: toMonthExpression(year, month),
|
||||||
|
children: dayNodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
key: nodeKey.year(year),
|
||||||
|
title: String(year),
|
||||||
|
level: 'year',
|
||||||
|
year,
|
||||||
|
expression: toYearExpression(year),
|
||||||
|
children: monthNodes
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const collectAllKeys = (nodes, keys = []) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
keys.push(node.key)
|
||||||
|
if (node.children?.length) collectAllKeys(node.children, keys)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
const collectLeafKeys = (nodes, keys = []) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (!node.children?.length) keys.push(node.key)
|
||||||
|
else collectLeafKeys(node.children, keys)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexNodesByKey = (nodes, map = new Map()) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
map.set(node.key, node)
|
||||||
|
if (node.children?.length) indexNodesByKey(node.children, map)
|
||||||
|
}
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prefer fully-selected ancestors over listing every leaf (Excel-style). */
|
||||||
|
const collectCollapsedExpressions = (nodes, checkedSet) => {
|
||||||
|
const expressions = []
|
||||||
|
|
||||||
|
const walk = (nodeList) => {
|
||||||
|
for (const node of nodeList) {
|
||||||
|
const leafKeys = collectLeafKeys([node])
|
||||||
|
const allChecked =
|
||||||
|
leafKeys.length > 0 && leafKeys.every((key) => checkedSet.has(key))
|
||||||
|
|
||||||
|
if (allChecked) {
|
||||||
|
expressions.push(node.expression)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node.children?.length) walk(node.children)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
walk(nodes)
|
||||||
|
return expressions
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterTreeBySearch = (nodes, query) => {
|
||||||
|
if (!query) return nodes
|
||||||
|
|
||||||
|
const filterNodes = (list) => {
|
||||||
|
const result = []
|
||||||
|
for (const node of list) {
|
||||||
|
const titleMatch = String(node.title).toLowerCase().includes(query)
|
||||||
|
if (titleMatch) {
|
||||||
|
result.push(node)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const filteredChildren = node.children?.length
|
||||||
|
? filterNodes(node.children)
|
||||||
|
: []
|
||||||
|
if (filteredChildren.length > 0) {
|
||||||
|
result.push({ ...node, children: filteredChildren })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterNodes(nodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseExpressionToKeys = (expression, nodeByKey) => {
|
||||||
|
const text = String(expression).trim()
|
||||||
|
if (!text) return []
|
||||||
|
|
||||||
|
// Year: 1 1 Y..31 12 Y
|
||||||
|
let match = text.match(/^1 1 (\d+)\.\.31 12 \1$/)
|
||||||
|
if (match) {
|
||||||
|
const key = nodeKey.year(Number(match[1]))
|
||||||
|
return nodeByKey.has(key) ? [key] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Month: 1 M Y..last M Y
|
||||||
|
match = text.match(/^1 (\d+) (\d+)\.\.(\d+) \1 \2$/)
|
||||||
|
if (match) {
|
||||||
|
const month = Number(match[1])
|
||||||
|
const year = Number(match[2])
|
||||||
|
const last = Number(match[3])
|
||||||
|
if (last === lastDayOfMonth(year, month)) {
|
||||||
|
const key = nodeKey.month(year, month)
|
||||||
|
return nodeByKey.has(key) ? [key] : []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Day-first numeric: D M Y [H [Min [S]]]
|
||||||
|
match = text.match(
|
||||||
|
/^(\d+)\s+(\d+)\s+(\d+)(?:\s+(\d+)(?:\s+(\d+)(?:\s+(\d+))?)?)?$/
|
||||||
|
)
|
||||||
|
if (match) {
|
||||||
|
const day = Number(match[1])
|
||||||
|
const month = Number(match[2])
|
||||||
|
const year = Number(match[3])
|
||||||
|
const hour = match[4] != null ? Number(match[4]) : null
|
||||||
|
const minute = match[5] != null ? Number(match[5]) : null
|
||||||
|
const second = match[6] != null ? Number(match[6]) : null
|
||||||
|
|
||||||
|
let key
|
||||||
|
if (second != null) {
|
||||||
|
key = nodeKey.second(year, month, day, hour, minute, second)
|
||||||
|
} else if (minute != null) {
|
||||||
|
key = nodeKey.minute(year, month, day, hour, minute)
|
||||||
|
} else if (hour != null) {
|
||||||
|
key = nodeKey.hour(year, month, day, hour)
|
||||||
|
} else {
|
||||||
|
key = nodeKey.day(year, month, day)
|
||||||
|
}
|
||||||
|
return nodeByKey.has(key) ? [key] : []
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interval spanning a single calendar day already handled; try ISO / Date parse
|
||||||
|
// for a specific instant → check the deepest matching node.
|
||||||
|
if (/[-/T]/.test(text) || /\d:\d/.test(text)) {
|
||||||
|
const date = parseDateValue(text)
|
||||||
|
if (!date) return []
|
||||||
|
const y = date.getFullYear()
|
||||||
|
const m = date.getMonth() + 1
|
||||||
|
const d = date.getDate()
|
||||||
|
const h = date.getHours()
|
||||||
|
const min = date.getMinutes()
|
||||||
|
const s = date.getSeconds()
|
||||||
|
|
||||||
|
const candidates = [
|
||||||
|
nodeKey.second(y, m, d, h, min, s),
|
||||||
|
nodeKey.minute(y, m, d, h, min),
|
||||||
|
nodeKey.hour(y, m, d, h),
|
||||||
|
nodeKey.day(y, m, d)
|
||||||
|
]
|
||||||
|
for (const key of candidates) {
|
||||||
|
if (nodeByKey.has(key)) return [key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const expandKeysForChecked = (keys, nodeByKey) => {
|
||||||
|
const checked = new Set()
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
const node = nodeByKey.get(key)
|
||||||
|
if (!node) {
|
||||||
|
checked.add(key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for (const leaf of collectLeafKeys([node])) {
|
||||||
|
checked.add(leaf)
|
||||||
|
}
|
||||||
|
// Also mark intermediate keys so Tree shows parent check state correctly
|
||||||
|
checked.add(key)
|
||||||
|
if (node.children?.length) {
|
||||||
|
for (const childKey of collectAllKeys(node.children)) {
|
||||||
|
checked.add(childKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...checked]
|
||||||
|
}
|
||||||
|
|
||||||
|
const SimpleDateTimePropertyFilter = ({
|
||||||
|
modelType,
|
||||||
|
propertyName,
|
||||||
|
value = [],
|
||||||
|
onChange,
|
||||||
|
search = ''
|
||||||
|
}) => {
|
||||||
|
const { getModelPropertyValues } = useContext(ApiServerContext)
|
||||||
|
const [dates, setDates] = useState([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [localChecked, setLocalChecked] = useState(null)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false
|
||||||
|
|
||||||
|
const loadOptions = async () => {
|
||||||
|
if (!modelType || !propertyName) return
|
||||||
|
setLoading(true)
|
||||||
|
try {
|
||||||
|
const values = await getModelPropertyValues(modelType, propertyName)
|
||||||
|
if (cancelled) return
|
||||||
|
const parsed = (values || [])
|
||||||
|
.map(parseDateValue)
|
||||||
|
.filter(Boolean)
|
||||||
|
.sort((a, b) => a.getTime() - b.getTime())
|
||||||
|
setDates(parsed)
|
||||||
|
} finally {
|
||||||
|
if (!cancelled) setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadOptions()
|
||||||
|
return () => {
|
||||||
|
cancelled = true
|
||||||
|
}
|
||||||
|
}, [modelType, propertyName, getModelPropertyValues])
|
||||||
|
|
||||||
|
const treeData = useMemo(
|
||||||
|
() => buildTreeData(buildDateHierarchy(dates)),
|
||||||
|
[dates]
|
||||||
|
)
|
||||||
|
|
||||||
|
const nodeByKey = useMemo(() => indexNodesByKey(treeData), [treeData])
|
||||||
|
const allKeys = useMemo(() => collectAllKeys(treeData), [treeData])
|
||||||
|
const allLeafKeys = useMemo(() => collectLeafKeys(treeData), [treeData])
|
||||||
|
|
||||||
|
const valueKey = useMemo(
|
||||||
|
() => JSON.stringify(value || []),
|
||||||
|
[value]
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (treeData.length === 0) return
|
||||||
|
|
||||||
|
if (value?.length > 0) {
|
||||||
|
const matched = []
|
||||||
|
for (const expr of value) {
|
||||||
|
matched.push(...parseExpressionToKeys(expr, nodeByKey))
|
||||||
|
}
|
||||||
|
if (matched.length > 0) {
|
||||||
|
setLocalChecked(expandKeysForChecked(matched, nodeByKey))
|
||||||
|
} else {
|
||||||
|
// Unrecognized expression — treat as no tree selection highlight
|
||||||
|
setLocalChecked([])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setLocalChecked(allKeys)
|
||||||
|
}
|
||||||
|
// valueKey captures value contents
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [treeData, valueKey, allKeys, nodeByKey])
|
||||||
|
|
||||||
|
const filteredTreeData = useMemo(() => {
|
||||||
|
const query = search.trim().toLowerCase()
|
||||||
|
return filterTreeBySearch(treeData, query)
|
||||||
|
}, [treeData, search])
|
||||||
|
|
||||||
|
const checkedKeys = localChecked ?? allKeys
|
||||||
|
|
||||||
|
const emitChange = useCallback(
|
||||||
|
(nextKeys) => {
|
||||||
|
setLocalChecked(nextKeys)
|
||||||
|
const checkedSet = new Set(nextKeys)
|
||||||
|
|
||||||
|
if (
|
||||||
|
allLeafKeys.length > 0 &&
|
||||||
|
allLeafKeys.every((key) => checkedSet.has(key))
|
||||||
|
) {
|
||||||
|
onChange?.([])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const expressions = collectCollapsedExpressions(treeData, checkedSet)
|
||||||
|
onChange?.(expressions)
|
||||||
|
},
|
||||||
|
[allLeafKeys, onChange, treeData]
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleCheck = (checked) => {
|
||||||
|
const next = Array.isArray(checked) ? checked : checked.checked
|
||||||
|
const query = search.trim().toLowerCase()
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
emitChange(next)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preserve checked leaves that are hidden by the search filter
|
||||||
|
const visibleLeafKeys = new Set(collectLeafKeys(filteredTreeData))
|
||||||
|
const hiddenSelectedLeaves = checkedKeys.filter((key) => {
|
||||||
|
const node = nodeByKey.get(key)
|
||||||
|
return node && !node.children?.length && !visibleLeafKeys.has(key)
|
||||||
|
})
|
||||||
|
const visibleSelectedLeaves = next.filter((key) => {
|
||||||
|
const node = nodeByKey.get(key)
|
||||||
|
return node && !node.children?.length
|
||||||
|
})
|
||||||
|
|
||||||
|
emitChange(
|
||||||
|
expandKeysForChecked(
|
||||||
|
[...hiddenSelectedLeaves, ...visibleSelectedLeaves],
|
||||||
|
nodeByKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Spin spinning={loading} indicator={<LoadingOutlined spin />}>
|
||||||
|
{treeData.length > 0 ? (
|
||||||
|
<Tree
|
||||||
|
checkable
|
||||||
|
selectable={false}
|
||||||
|
treeData={filteredTreeData}
|
||||||
|
checkedKeys={checkedKeys}
|
||||||
|
onCheck={handleCheck}
|
||||||
|
defaultExpandAll={false}
|
||||||
|
style={{ minWidth: 0 }}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</Spin>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
SimpleDateTimePropertyFilter.propTypes = {
|
||||||
|
modelType: PropTypes.string,
|
||||||
|
propertyName: PropTypes.string,
|
||||||
|
value: PropTypes.array,
|
||||||
|
onChange: PropTypes.func,
|
||||||
|
search: PropTypes.string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SimpleDateTimePropertyFilter
|
||||||
@ -7,9 +7,13 @@ import {
|
|||||||
getModelProperties
|
getModelProperties
|
||||||
} from '../../../database/ObjectModels'
|
} from '../../../database/ObjectModels'
|
||||||
import ObjectProperty from './ObjectProperty'
|
import ObjectProperty from './ObjectProperty'
|
||||||
|
import SimpleDateTimePropertyFilter from './SimpleDateTimePropertyFilter'
|
||||||
import { LoadingOutlined } from '@ant-design/icons'
|
import { LoadingOutlined } from '@ant-design/icons'
|
||||||
const { Text } = Typography
|
const { Text } = Typography
|
||||||
|
|
||||||
|
const isDateTimeProperty = (property) =>
|
||||||
|
property?.type === 'dateTime' || property?.type === 'date'
|
||||||
|
|
||||||
const getOptionKey = (option) => {
|
const getOptionKey = (option) => {
|
||||||
if (option && typeof option === 'object') {
|
if (option && typeof option === 'object') {
|
||||||
if (option.objectType) {
|
if (option.objectType) {
|
||||||
@ -63,7 +67,7 @@ const SimplePropertyFilter = ({
|
|||||||
let cancelled = false
|
let cancelled = false
|
||||||
|
|
||||||
const loadOptions = async () => {
|
const loadOptions = async () => {
|
||||||
if (!modelType || !propertyName) return
|
if (!modelType || !propertyName || isDateTimeProperty(property)) return
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const values = await getModelPropertyValues(modelType, propertyName)
|
const values = await getModelPropertyValues(modelType, propertyName)
|
||||||
@ -81,7 +85,7 @@ const SimplePropertyFilter = ({
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [modelType, propertyName, getModelPropertyValues])
|
}, [modelType, propertyName, getModelPropertyValues, property])
|
||||||
|
|
||||||
const valueKey = useMemo(
|
const valueKey = useMemo(
|
||||||
() => JSON.stringify((value || []).map(getOptionKey)),
|
() => JSON.stringify((value || []).map(getOptionKey)),
|
||||||
@ -132,6 +136,18 @@ const SimplePropertyFilter = ({
|
|||||||
emitChange([...hiddenSelected, ...visibleCheckedKeys])
|
emitChange([...hiddenSelected, ...visibleCheckedKeys])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isDateTimeProperty(property)) {
|
||||||
|
return (
|
||||||
|
<SimpleDateTimePropertyFilter
|
||||||
|
modelType={modelType}
|
||||||
|
propertyName={propertyName}
|
||||||
|
value={value}
|
||||||
|
onChange={onChange}
|
||||||
|
search={search}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Spin spinning={loading} indicator={<LoadingOutlined spin />}>
|
<Spin spinning={loading} indicator={<LoadingOutlined spin />}>
|
||||||
<Checkbox.Group
|
<Checkbox.Group
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user