diff --git a/src/backend/utils/filter/filter.ts b/src/backend/utils/filter/filter.ts index 8e45fd333..f9c282690 100644 --- a/src/backend/utils/filter/filter.ts +++ b/src/backend/utils/filter/filter.ts @@ -6,6 +6,21 @@ import { ActionContext } from '../../actions/index.js' export const PARAM_SEPARATOR = '~~' +export const OPERATOR_SEPARATOR = '~' + +export const MATCHING_PATTERNS = { + EQ: 'equals', + NE: 'notEquals', + CO: 'contains', + EW: 'endsWith', + SW: 'startsWith', +} + +export const OPERATORS = { + AND: 'and', + OR: 'or', +} + export type FilterElement = { path: string; property: BaseProperty; diff --git a/src/frontend/components/property-type/default-type/filter.tsx b/src/frontend/components/property-type/default-type/filter.tsx index f023b88d4..960641c72 100644 --- a/src/frontend/components/property-type/default-type/filter.tsx +++ b/src/frontend/components/property-type/default-type/filter.tsx @@ -1,27 +1,113 @@ import React from 'react' -import { FormGroup, Input, Select } from '@adminjs/design-system' +import { Box, FormGroup, Input, Select } from '@adminjs/design-system' import allowOverride from '../../../hoc/allow-override.js' import { FilterPropertyProps } from '../base-property-props.js' import PropertyLabel from '../utils/property-label/property-label.js' import { useTranslation } from '../../../hooks/use-translation.js' +import * as BackendFilter from '../../../../backend/utils/filter/filter.js' + +const { MATCHING_PATTERNS, OPERATOR_SEPARATOR, OPERATORS, PARAM_SEPARATOR } = BackendFilter + +const getKey = (path:string, patternMatching: string, operator:string) => { + if (patternMatching === MATCHING_PATTERNS.CO) { + if (operator === OPERATORS.AND) { + return path + } + + return `${path}${PARAM_SEPARATOR}${operator}` + } + if (operator === OPERATORS.AND) { + return `${path}${PARAM_SEPARATOR}${patternMatching}` + } + return `${path}${PARAM_SEPARATOR}${operator}${OPERATOR_SEPARATOR}${patternMatching}` +} + +const generatePossibleKeys = (path:string) => { + const possibleKeys:string[] = [] + + for (const matchingPattern of Object.values(MATCHING_PATTERNS)) { + for (const operator of Object.values(OPERATORS)) { + possibleKeys.push(getKey(path, matchingPattern, operator)) + } + } + + return possibleKeys +} const Filter: React.FC = (props) => { const { property, onChange, filter } = props + + const possibleKeys = generatePossibleKeys(property.path) + + const [currentKey, currentInput] = Object.entries(filter).find( + ([key]) => possibleKeys.includes(key), + ) || [] + let currentPatternMatching = MATCHING_PATTERNS.CO + let currentOperator = OPERATORS.AND + + if (currentKey) { + const filterProperties = currentKey.split(PARAM_SEPARATOR) + const param = (filterProperties[1] || '').split(OPERATOR_SEPARATOR) + const tentativeOperator = param[0] || '' + const tentativeMatchingPattern = (param.length > 1 ? param[1] : param[0]) || '' + currentOperator = Object.values(OPERATORS).includes(tentativeOperator) + ? tentativeOperator : OPERATORS.AND + currentPatternMatching = Object.values(MATCHING_PATTERNS).includes(tentativeMatchingPattern) + ? tentativeMatchingPattern : MATCHING_PATTERNS.CO + } + const { tl } = useTranslation() const handleInputChange = (event) => { onChange(property.path, event.target.value) } + const handleInputInComboChange = (event) => { + const key = getKey(property.path, currentPatternMatching, currentOperator) + onChange(key, event.target.value) + } + const handleSelectChange = (selected) => { const value = selected ? selected.value : '' onChange(property.path, value) } + const handleSelectPatternMatchingInComboChange = (selected) => { + const changedKey = getKey( + property.path, + selected?.value || MATCHING_PATTERNS.CO, + currentOperator, + ) + + possibleKeys.forEach((key) => { + if (key !== changedKey) { + delete filter[key] + } + }) + onChange(changedKey, currentInput || '') + } + + const handleSelectOperatorInComboChange = (selected) => { + const changedKey = getKey( + property.path, + currentPatternMatching, + selected?.value || OPERATORS.AND, + ) + + possibleKeys.forEach((key) => { + if (key !== changedKey) { + delete filter[key] + } + }) + onChange(changedKey, currentInput || '') + } + const renderInput = () => { - const filterKey = `filter-${property.path}` - const value = filter[property.path] || '' + const valueKey = getKey(property.path, currentPatternMatching, currentOperator) + + const filterKey = `filter-${valueKey}` + const value = filter[valueKey] || '' if (property.availableValues) { const availableValues = property.availableValues.map((v) => ({ ...v, @@ -40,6 +126,67 @@ const Filter: React.FC = (props) => { /> ) } + + if (property.type === 'string') { + const patternMatching = { label: currentPatternMatching, value: currentPatternMatching } + const operator = { label: currentOperator, value: currentOperator } + return ( + + + + + + + + + ) + } + return (