Add rating filter/sort + fix isEmpty/isNotEmpty + fix combinedViewFilters (#6310)
## Context - Adding RATING sort and filter capabilities. - Fixing isEmpty/isNotEmpty filters - Fixing combined view filters so it combines filters per field metadata and not per filter id. This is more a product question but to me it does not make sense to apply multiples filters on the same field IF the operations is wrapped in a AND. If at some point we want to put a OR instead then that would make more sense
This commit is contained in:
@ -5,6 +5,7 @@ import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/
|
||||
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
|
||||
import { ObjectFilterDropdownRatingInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
|
||||
import { MultipleFiltersDropdownFilterOnFilterChangedEffect } from './MultipleFiltersDropdownFilterOnFilterChangedEffect';
|
||||
import { ObjectFilterDropdownDateInput } from './ObjectFilterDropdownDateInput';
|
||||
import { ObjectFilterDropdownFilterSelect } from './ObjectFilterDropdownFilterSelect';
|
||||
@ -70,6 +71,9 @@ export const MultipleFiltersDropdownContent = ({
|
||||
{['NUMBER', 'CURRENCY'].includes(
|
||||
filterDefinitionUsedInDropdown.type,
|
||||
) && <ObjectFilterDropdownNumberInput />}
|
||||
{filterDefinitionUsedInDropdown.type === 'RATING' && (
|
||||
<ObjectFilterDropdownRatingInput />
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'DATE_TIME' && (
|
||||
<ObjectFilterDropdownDateInput />
|
||||
)}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues';
|
||||
import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { RatingInput } from '@/ui/field/input/components/RatingInput';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
|
||||
const convertFieldRatingValueToNumber = (rating: FieldRatingValue): string => {
|
||||
return rating.split('_')[1];
|
||||
};
|
||||
|
||||
export const convertGreaterThanRatingToArrayOfRatingValues = (
|
||||
greaterThanValue: number,
|
||||
) => {
|
||||
return RATING_VALUES.filter((_, index) => index + 1 > greaterThanValue);
|
||||
};
|
||||
|
||||
export const convertLessThanRatingToArrayOfRatingValues = (
|
||||
lessThanValue: number,
|
||||
) => {
|
||||
return RATING_VALUES.filter((_, index) => index + 1 <= lessThanValue);
|
||||
};
|
||||
|
||||
export const convertRatingToRatingValue = (rating: number) => {
|
||||
return `RATING_${rating}`;
|
||||
};
|
||||
|
||||
export const ObjectFilterDropdownRatingInput = () => {
|
||||
const {
|
||||
selectedOperandInDropdownState,
|
||||
filterDefinitionUsedInDropdownState,
|
||||
selectedFilterState,
|
||||
selectFilter,
|
||||
} = useFilterDropdown();
|
||||
|
||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||
filterDefinitionUsedInDropdownState,
|
||||
);
|
||||
const selectedOperandInDropdown = useRecoilValue(
|
||||
selectedOperandInDropdownState,
|
||||
);
|
||||
|
||||
const selectedFilter = useRecoilValue(selectedFilterState);
|
||||
|
||||
return (
|
||||
filterDefinitionUsedInDropdown &&
|
||||
selectedOperandInDropdown && (
|
||||
<DropdownMenuItemsContainer>
|
||||
<RatingInput
|
||||
value={selectedFilter?.value as FieldRatingValue}
|
||||
onChange={(newValue: FieldRatingValue) => {
|
||||
selectFilter?.({
|
||||
id: selectedFilter?.id ? selectedFilter.id : v4(),
|
||||
fieldMetadataId: filterDefinitionUsedInDropdown.fieldMetadataId,
|
||||
value: convertFieldRatingValueToNumber(newValue),
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: convertFieldRatingValueToNumber(newValue),
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
)
|
||||
);
|
||||
};
|
||||
@ -12,4 +12,5 @@ export type FilterType =
|
||||
| 'RELATION'
|
||||
| 'ADDRESS'
|
||||
| 'SELECT'
|
||||
| 'RATING'
|
||||
| 'MULTI_SELECT';
|
||||
|
||||
@ -34,6 +34,13 @@ export const getOperandsForFilterType = (
|
||||
ViewFilterOperand.LessThan,
|
||||
...emptyOperands,
|
||||
];
|
||||
case 'RATING':
|
||||
return [
|
||||
ViewFilterOperand.Is,
|
||||
ViewFilterOperand.GreaterThan,
|
||||
ViewFilterOperand.LessThan,
|
||||
...emptyOperands,
|
||||
];
|
||||
case 'RELATION':
|
||||
return [...relationOperands, ...emptyOperands];
|
||||
case 'SELECT':
|
||||
|
||||
@ -143,6 +143,7 @@ export const isRecordMatchingFilter = ({
|
||||
case FieldMetadataType.Email:
|
||||
case FieldMetadataType.Phone:
|
||||
case FieldMetadataType.Select:
|
||||
case FieldMetadataType.Rating:
|
||||
case FieldMetadataType.MultiSelect:
|
||||
case FieldMetadataType.Text: {
|
||||
return isMatchingStringFilter({
|
||||
|
||||
@ -18,6 +18,11 @@ import { Field } from '~/generated/graphql';
|
||||
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
import {
|
||||
convertGreaterThanRatingToArrayOfRatingValues,
|
||||
convertLessThanRatingToArrayOfRatingValues,
|
||||
convertRatingToRatingValue,
|
||||
} from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
|
||||
import { Filter } from '../../object-filter-dropdown/types/Filter';
|
||||
|
||||
export type ObjectDropdownFilter = Omit<Filter, 'definition'> & {
|
||||
@ -187,6 +192,11 @@ const applyEmptyFilters = (
|
||||
[correspondingField.name]: { is: 'NULL' } as FloatFilter,
|
||||
};
|
||||
break;
|
||||
case 'RATING':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name]: { is: 'NULL' } as StringFilter,
|
||||
};
|
||||
break;
|
||||
case 'DATE_TIME':
|
||||
emptyRecordFilter = {
|
||||
[correspondingField.name]: { is: 'NULL' } as DateFilter,
|
||||
@ -313,6 +323,48 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'RATING':
|
||||
switch (rawUIFilter.operand) {
|
||||
case ViewFilterOperand.Is:
|
||||
objectRecordFilters.push({
|
||||
[correspondingField.name]: {
|
||||
eq: convertRatingToRatingValue(parseFloat(rawUIFilter.value)),
|
||||
} as StringFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.GreaterThan:
|
||||
objectRecordFilters.push({
|
||||
[correspondingField.name]: {
|
||||
in: convertGreaterThanRatingToArrayOfRatingValues(
|
||||
parseFloat(rawUIFilter.value),
|
||||
),
|
||||
} as StringFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.LessThan:
|
||||
objectRecordFilters.push({
|
||||
[correspondingField.name]: {
|
||||
in: convertLessThanRatingToArrayOfRatingValues(
|
||||
parseFloat(rawUIFilter.value),
|
||||
),
|
||||
} as StringFilter,
|
||||
});
|
||||
break;
|
||||
case ViewFilterOperand.IsEmpty:
|
||||
case ViewFilterOperand.IsNotEmpty:
|
||||
applyEmptyFilters(
|
||||
rawUIFilter.operand,
|
||||
correspondingField,
|
||||
objectRecordFilters,
|
||||
rawUIFilter.definition.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.type} filter`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 'NUMBER':
|
||||
switch (rawUIFilter.operand) {
|
||||
case ViewFilterOperand.GreaterThan:
|
||||
|
||||
Reference in New Issue
Block a user