From 8f12aea64a13fa043341c04364826bba61f9e5a9 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Wed, 22 Nov 2023 17:23:10 +0100 Subject: [PATCH] Enrich filters with all types (#2653) --- .../components/KeyboardShortcutMenu.tsx | 2 +- ...atFieldMetadataItemsAsFilterDefinitions.ts | 33 ++- .../{ => components}/RelationPicker.tsx | 14 +- .../components/RelationFieldDisplay.tsx | 3 +- .../meta-types/hooks/useRelationField.ts | 6 - .../input/components/RelationFieldInput.tsx | 2 +- .../MultipleFiltersDropdownButton.tsx | 4 + .../MultipleFiltersDropdownContent.tsx | 10 +- .../components/ObjectFilterDropdownButton.tsx | 2 +- ...ObjectFilterDropdownEntitySearchSelect.tsx | 9 +- .../ObjectFilterDropdownEntitySelect.tsx | 47 +++- .../ObjectFilterDropdownFilterSelect.tsx | 38 ++-- ...SingleEntityObjectFilterDropdownButton.tsx | 1 + .../object-filter-dropdown/hooks/useFilter.ts | 13 ++ .../types/FilterType.ts | 8 +- .../utils/getOperandsForFilterType.ts | 5 +- .../utils/turnFiltersIntoWhereClauseV2.ts | 214 ++++++++++++++---- .../views/hooks/internal/useViewFilters.ts | 7 +- .../opportunityBoardFilterDefinitions.tsx | 6 +- .../pages/tasks/tasks-filter-definitions.tsx | 2 +- 20 files changed, 315 insertions(+), 111 deletions(-) rename front/src/modules/ui/input/components/internal/relation-picker/{ => components}/RelationPicker.tsx (87%) diff --git a/front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx b/front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx index 0ae8bb2fb..15d988a03 100644 --- a/front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx +++ b/front/src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenu.tsx @@ -20,7 +20,7 @@ export const KeyboardShortcutMenu = () => { isKeyboardShortcutMenuOpenedState, ); useScopedHotkeys( - 'shift+?,meta+?,esc', + 'shift+?,meta+?', () => { toggleKeyboardShortcutMenu(); }, diff --git a/front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts b/front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts index bdd1f4f08..8609f7417 100644 --- a/front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts +++ b/front/src/modules/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions.ts @@ -12,14 +12,29 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({ if ( ![ FieldMetadataType.DateTime, - FieldMetadataType.Number, - FieldMetadataType.Currency, FieldMetadataType.Text, - ].includes(field.type) || - field.name === 'probability' + FieldMetadataType.Email, + FieldMetadataType.Number, + FieldMetadataType.Link, + FieldMetadataType.FullName, + FieldMetadataType.Relation, + FieldMetadataType.Currency, + ].includes(field.type) ) { return acc; } + + // Todo: remove once Rating fieldtype is implemented + if (field.name === 'probability') { + return acc; + } + + if (field.type === FieldMetadataType.Relation) { + if (field.fromRelationMetadata) { + return acc; + } + } + return [...acc, formatFieldMetadataItemAsFilterDefinition({ field })]; }, [] as FilterDefinition[]); @@ -34,9 +49,19 @@ const formatFieldMetadataItemAsFilterDefinition = ({ type: field.type === FieldMetadataType.DateTime ? 'DATE_TIME' + : field.type === FieldMetadataType.Link + ? 'LINK' + : field.type === FieldMetadataType.FullName + ? 'FULL_NAME' : field.type === FieldMetadataType.Number ? 'NUMBER' : field.type === FieldMetadataType.Currency ? 'CURRENCY' + : field.type === FieldMetadataType.Email + ? 'TEXT' + : field.type === FieldMetadataType.Phone + ? 'TEXT' + : field.type === FieldMetadataType.Relation + ? 'RELATION' : 'TEXT', }); diff --git a/front/src/modules/ui/input/components/internal/relation-picker/RelationPicker.tsx b/front/src/modules/ui/input/components/internal/relation-picker/components/RelationPicker.tsx similarity index 87% rename from front/src/modules/ui/input/components/internal/relation-picker/RelationPicker.tsx rename to front/src/modules/ui/input/components/internal/relation-picker/components/RelationPicker.tsx index 53d93e974..5905205b2 100644 --- a/front/src/modules/ui/input/components/internal/relation-picker/RelationPicker.tsx +++ b/front/src/modules/ui/input/components/internal/relation-picker/components/RelationPicker.tsx @@ -4,16 +4,16 @@ import { useQuery } from '@apollo/client'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { IconUserCircle } from '@/ui/display/icon'; +import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker'; import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect'; import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; -import { useRelationField } from '@/ui/object/field/meta-types/hooks/useRelationField'; import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition'; import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; export type RelationPickerProps = { - recordId: string; + recordId?: string; onSubmit: (newUser: EntityForSelect | null) => void; onCancel?: () => void; width?: number; @@ -43,9 +43,9 @@ export const RelationPicker = ({ const useFindManyQuery = (options: any) => useQuery(findManyQuery, options); - const { identifiersMapper, searchQuery } = useRelationField(); + const { identifiersMapper, searchQuery } = useRelationPicker(); - const workspaceMembers = useFilteredSearchEntityQuery({ + const records = useFilteredSearchEntityQuery({ queryHook: useFindManyQuery, filters: [ { @@ -74,11 +74,11 @@ export const RelationPicker = ({ ); diff --git a/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx b/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx index 4495dd58d..dad9570ca 100644 --- a/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx +++ b/front/src/modules/ui/object/field/meta-types/display/components/RelationFieldDisplay.tsx @@ -1,11 +1,12 @@ import { EntityChip } from '@/ui/display/chip/components/EntityChip'; +import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker'; import { useRelationField } from '../../hooks/useRelationField'; export const RelationFieldDisplay = () => { const { fieldValue, fieldDefinition } = useRelationField(); - const { identifiersMapper } = useRelationField(); + const { identifiersMapper } = useRelationPicker(); if (!fieldValue || !fieldDefinition || !identifiersMapper) { return <>; diff --git a/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts b/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts index 47541b1b4..a48f60a26 100644 --- a/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts +++ b/front/src/modules/ui/object/field/meta-types/hooks/useRelationField.ts @@ -1,8 +1,6 @@ import { useContext } from 'react'; import { useRecoilState } from 'recoil'; -import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker'; - import { FieldContext } from '../../contexts/FieldContext'; import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; @@ -32,15 +30,11 @@ export const useRelationField = () => { const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue; - const { identifiersMapper, searchQuery } = useRelationPicker(); - return { fieldDefinition, fieldValue, initialValue, initialSearchValue, setFieldValue, - searchQuery, - identifiersMapper, }; }; diff --git a/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx b/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx index 76f43c537..71294ea40 100644 --- a/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx +++ b/front/src/modules/ui/object/field/meta-types/input/components/RelationFieldInput.tsx @@ -1,7 +1,7 @@ import { useEffect } from 'react'; import styled from '@emotion/styled'; -import { RelationPicker } from '@/ui/input/components/internal/relation-picker/RelationPicker'; +import { RelationPicker } from '@/ui/input/components/internal/relation-picker/components/RelationPicker'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { usePersistField } from '../../../hooks/usePersistField'; diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownButton.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownButton.tsx index ad5aad163..0c85e5f81 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownButton.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownButton.tsx @@ -1,5 +1,6 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; +import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter'; import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope'; import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId'; @@ -14,9 +15,12 @@ type MultipleFiltersDropdownButtonProps = { export const MultipleFiltersDropdownButton = ({ hotkeyScope, }: MultipleFiltersDropdownButtonProps) => { + const { resetFilter } = useFilter(); + return ( } dropdownComponents={} dropdownHotkeyScope={hotkeyScope} diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx index 46b4006ff..7a38ceca0 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/MultipleFiltersDropdownContent.tsx @@ -30,19 +30,19 @@ export const MultipleFiltersDropdownContent = () => { <> - {filterDefinitionUsedInDropdown.type === 'TEXT' && ( - - )} + {['TEXT', 'EMAIL', 'PHONE', 'FULL_NAME', 'LINK'].includes( + filterDefinitionUsedInDropdown.type, + ) && } {['NUMBER', 'CURRENCY'].includes( filterDefinitionUsedInDropdown.type, ) && } {filterDefinitionUsedInDropdown.type === 'DATE_TIME' && ( )} - {filterDefinitionUsedInDropdown.type === 'ENTITY' && ( + {filterDefinitionUsedInDropdown.type === 'RELATION' && ( )} - {filterDefinitionUsedInDropdown.type === 'ENTITY' && ( + {filterDefinitionUsedInDropdown.type === 'RELATION' && ( )} diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx index c00993935..9c2910da8 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownButton.tsx @@ -16,7 +16,7 @@ export const ObjectFilterDropdownButton = ({ const hasOnlyOneEntityFilter = availableFilterDefinitions.length === 1 && - availableFilterDefinitions[0].type === 'ENTITY'; + availableFilterDefinitions[0].type === 'RELATION'; if (!availableFilterDefinitions.length) { return <>; diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect.tsx index 9e1b2b627..d93bb0d4d 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from 'react'; import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect'; import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { useFilter } from '../hooks/useFilter'; @@ -21,9 +22,11 @@ export const ObjectFilterDropdownEntitySearchSelect = ({ selectFilter, } = useFilter(); + const { closeDropdown } = useDropdown(); + const [isAllEntitySelected, setIsAllEntitySelected] = useState(false); - const handleUserSelected = ( + const handleRecordSelected = ( selectedEntity: EntityForSelect | null | undefined, ) => { if ( @@ -39,6 +42,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({ } setObjectFilterDropdownSelectedEntityId(selectedEntity.id); + closeDropdown(); selectFilter?.({ displayValue: selectedEntity.name, @@ -69,6 +73,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({ setIsAllEntitySelected(true); setObjectFilterDropdownSelectedEntityId(null); + closeDropdown(); selectFilter?.({ displayValue: filterDefinitionUsedInDropdown.selectAllLabel, @@ -100,7 +105,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({ entitiesToSelect={entitiesForSelect.entitiesToSelect} selectedEntity={entitiesForSelect.selectedEntities[0]} loading={entitiesForSelect.loading} - onEntitySelected={handleUserSelected} + onEntitySelected={handleRecordSelected} SelectAllIcon={filterDefinitionUsedInDropdown?.SelectAllIcon} selectAllLabel={filterDefinitionUsedInDropdown?.selectAllLabel} isAllEntitySelected={isAllEntitySelected} diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySelect.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySelect.tsx index df846f817..e01825539 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySelect.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySelect.tsx @@ -1,21 +1,52 @@ -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; +import { useQuery } from '@apollo/client'; + +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; +import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker'; +import { ObjectFilterDropdownEntitySearchSelect } from '@/ui/object/object-filter-dropdown/components/ObjectFilterDropdownEntitySearchSelect'; import { useFilter } from '../hooks/useFilter'; export const ObjectFilterDropdownEntitySelect = () => { - const { filterDefinitionUsedInDropdown } = useFilter(); + const { + filterDefinitionUsedInDropdown, + objectFilterDropdownSearchInput, + objectFilterDropdownSelectedEntityId, + } = useFilter(); - if (filterDefinitionUsedInDropdown?.type !== 'ENTITY') { + const { findManyQuery } = useObjectMetadataItem({ + objectNameSingular: 'company', + }); + + const useFindManyQuery = (options: any) => useQuery(findManyQuery, options); + + const { identifiersMapper, searchQuery } = useRelationPicker(); + + const filteredSearchEntityResults = useFilteredSearchEntityQuery({ + queryHook: useFindManyQuery, + filters: [ + { + fieldNames: searchQuery?.computeFilterFields?.('company') ?? [], + filter: objectFilterDropdownSearchInput, + }, + ], + orderByField: 'createdAt', + selectedIds: objectFilterDropdownSelectedEntityId + ? [objectFilterDropdownSelectedEntityId] + : [], + mappingFunction: (record: any) => identifiersMapper?.(record, 'company'), + objectNamePlural: 'companies', + }); + + if (filterDefinitionUsedInDropdown?.type !== 'RELATION') { return null; } return ( <> - - - {filterDefinitionUsedInDropdown.entitySelectComponent} - + ); }; diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx index 91ad36b8e..ec44c2078 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect.tsx @@ -21,27 +21,29 @@ export const ObjectFilterDropdownFilterSelect = () => { return ( - {availableFilterDefinitions.map((availableFilterDefinition, index) => ( - { - setFilterDefinitionUsedInDropdown(availableFilterDefinition); + {[...availableFilterDefinitions] + .sort((a, b) => a.label.localeCompare(b.label)) + .map((availableFilterDefinition, index) => ( + { + setFilterDefinitionUsedInDropdown(availableFilterDefinition); - if (availableFilterDefinition.type === 'ENTITY') { - setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); - } + if (availableFilterDefinition.type === 'RELATION') { + setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); + } - setSelectedOperandInDropdown( - getOperandsForFilterType(availableFilterDefinition.type)?.[0], - ); + setSelectedOperandInDropdown( + getOperandsForFilterType(availableFilterDefinition.type)?.[0], + ); - setObjectFilterDropdownSearchInput(''); - }} - LeftIcon={icons[availableFilterDefinition.iconName]} - text={availableFilterDefinition.label} - /> - ))} + setObjectFilterDropdownSearchInput(''); + }} + LeftIcon={icons[availableFilterDefinition.iconName]} + text={availableFilterDefinition.label} + /> + ))} ); }; diff --git a/front/src/modules/ui/object/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx b/front/src/modules/ui/object/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx index a3f799a3b..678cfd0ef 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx +++ b/front/src/modules/ui/object/object-filter-dropdown/components/SingleEntityObjectFilterDropdownButton.tsx @@ -25,6 +25,7 @@ export const SingleEntityObjectFilterDropdownButton = ({ selectedFilter, setFilterDefinitionUsedInDropdown, setSelectedOperandInDropdown, + resetFilter, } = useFilter(); const availableFilter = availableFilterDefinitions[0]; diff --git a/front/src/modules/ui/object/object-filter-dropdown/hooks/useFilter.ts b/front/src/modules/ui/object/object-filter-dropdown/hooks/useFilter.ts index 686e38168..7ba1d7f8c 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/hooks/useFilter.ts +++ b/front/src/modules/ui/object/object-filter-dropdown/hooks/useFilter.ts @@ -48,6 +48,18 @@ export const useFilter = (props?: UseFilterProps) => { [setSelectedFilter, onFilterSelect], ); + const resetFilter = useCallback(() => { + setObjectFilterDropdownSearchInput(''); + setObjectFilterDropdownSelectedEntityId(null); + setSelectedFilter(undefined); + setSelectedOperandInDropdown(null); + }, [ + setObjectFilterDropdownSearchInput, + setObjectFilterDropdownSelectedEntityId, + setSelectedFilter, + setSelectedOperandInDropdown, + ]); + return { scopeId, availableFilterDefinitions, @@ -67,5 +79,6 @@ export const useFilter = (props?: UseFilterProps) => { selectedOperandInDropdown, setSelectedOperandInDropdown, selectFilter, + resetFilter, }; }; diff --git a/front/src/modules/ui/object/object-filter-dropdown/types/FilterType.ts b/front/src/modules/ui/object/object-filter-dropdown/types/FilterType.ts index 8d3c64cf0..2c182f0a8 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/types/FilterType.ts +++ b/front/src/modules/ui/object/object-filter-dropdown/types/FilterType.ts @@ -1,6 +1,10 @@ export type FilterType = | 'TEXT' + | 'PHONE' + | 'EMAIL' | 'DATE_TIME' - | 'ENTITY' | 'NUMBER' - | 'CURRENCY'; + | 'CURRENCY' + | 'FULL_NAME' + | 'LINK' + | 'RELATION'; diff --git a/front/src/modules/ui/object/object-filter-dropdown/utils/getOperandsForFilterType.ts b/front/src/modules/ui/object/object-filter-dropdown/utils/getOperandsForFilterType.ts index 752249c20..cd70c9334 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/utils/getOperandsForFilterType.ts +++ b/front/src/modules/ui/object/object-filter-dropdown/utils/getOperandsForFilterType.ts @@ -7,12 +7,15 @@ export const getOperandsForFilterType = ( ): ViewFilterOperand[] => { switch (filterType) { case 'TEXT': + case 'EMAIL': + case 'FULL_NAME': + case 'LINK': return [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]; case 'CURRENCY': case 'NUMBER': case 'DATE_TIME': return [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan]; - case 'ENTITY': + case 'RELATION': return [ViewFilterOperand.Is, ViewFilterOperand.IsNot]; default: return []; diff --git a/front/src/modules/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2.ts b/front/src/modules/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2.ts index f2b5c02b2..504dd79e2 100644 --- a/front/src/modules/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2.ts +++ b/front/src/modules/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2.ts @@ -13,7 +13,7 @@ export const turnFiltersIntoWhereClauseV2 = ( filters: FilterToTurnIntoWhereClause[], fields: Pick[], ) => { - const whereClause: Record = {}; + const whereClause: any[] = []; filters.forEach((filter) => { const correspondingField = fields.find( @@ -26,53 +26,25 @@ export const turnFiltersIntoWhereClauseV2 = ( } switch (filter.definition.type) { + case 'EMAIL': + case 'PHONE': case 'TEXT': switch (filter.operand) { case ViewFilterOperand.Contains: - whereClause[correspondingField.name] = { - eq: filter.value, - }; + whereClause.push({ + [correspondingField.name]: { + ilike: `%${filter.value}%`, + }, + }); return; case ViewFilterOperand.DoesNotContain: - whereClause[correspondingField.name] = { + whereClause.push({ not: { - eq: filter.value, + [correspondingField.name]: { + ilike: `%${filter.value}%`, + }, }, - }; - return; - default: - throw new Error( - `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, - ); - } - case 'NUMBER': - switch (filter.operand) { - case ViewFilterOperand.GreaterThan: - whereClause[correspondingField.name] = { - gte: parseFloat(filter.value), - }; - return; - case ViewFilterOperand.LessThan: - whereClause[correspondingField.name] = { - lte: parseFloat(filter.value), - }; - return; - default: - throw new Error( - `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, - ); - } - case 'CURRENCY': - switch (filter.operand) { - case ViewFilterOperand.GreaterThan: - whereClause[correspondingField.name] = { - amountMicros: { gte: parseFloat(filter.value) * 1000000 }, - }; - return; - case ViewFilterOperand.LessThan: - whereClause[correspondingField.name] = { - amountMicros: { lte: parseFloat(filter.value) * 1000000 }, - }; + }); return; default: throw new Error( @@ -82,14 +54,159 @@ export const turnFiltersIntoWhereClauseV2 = ( case 'DATE_TIME': switch (filter.operand) { case ViewFilterOperand.GreaterThan: - whereClause[correspondingField.name] = { - gte: filter.value, - }; + whereClause.push({ + [correspondingField.name]: { + gte: filter.value, + }, + }); return; case ViewFilterOperand.LessThan: - whereClause[correspondingField.name] = { - lte: filter.value, - }; + whereClause.push({ + [correspondingField.name]: { + lte: filter.value, + }, + }); + return; + default: + throw new Error( + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, + ); + } + case 'NUMBER': + switch (filter.operand) { + case ViewFilterOperand.GreaterThan: + whereClause.push({ + [correspondingField.name]: { + gte: parseFloat(filter.value), + }, + }); + return; + case ViewFilterOperand.LessThan: + whereClause.push({ + [correspondingField.name]: { + lte: parseFloat(filter.value), + }, + }); + return; + default: + throw new Error( + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, + ); + } + case 'RELATION': + switch (filter.operand) { + case ViewFilterOperand.Is: + whereClause.push({ + [correspondingField.name + 'Id']: { + eq: filter.value, + }, + }); + return; + case ViewFilterOperand.IsNot: + whereClause.push({ + [correspondingField.name + 'Id']: { + neq: filter.value, + }, + }); + return; + default: + throw new Error( + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, + ); + } + case 'CURRENCY': + switch (filter.operand) { + case ViewFilterOperand.GreaterThan: + whereClause.push({ + [correspondingField.name]: { + amountMicros: { gte: parseFloat(filter.value) * 1000000 }, + }, + }); + return; + case ViewFilterOperand.LessThan: + whereClause.push({ + [correspondingField.name]: { + amountMicros: { lte: parseFloat(filter.value) * 1000000 }, + }, + }); + return; + default: + throw new Error( + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, + ); + } + case 'LINK': + switch (filter.operand) { + case ViewFilterOperand.Contains: + whereClause.push({ + [correspondingField.name]: { + url: { + ilike: `%${filter.value}%`, + }, + }, + }); + return; + case ViewFilterOperand.DoesNotContain: + whereClause.push({ + not: { + [correspondingField.name]: { + url: { + ilike: `%${filter.value}%`, + }, + }, + }, + }); + return; + default: + throw new Error( + `Unknown operand ${filter.operand} for ${filter.definition.type} filter`, + ); + } + case 'FULL_NAME': + switch (filter.operand) { + case ViewFilterOperand.Contains: + whereClause.push({ + or: [ + { + [correspondingField.name]: { + firstName: { + ilike: `%${filter.value}%`, + }, + }, + }, + { + [correspondingField.name]: { + firstName: { + ilike: `%${filter.value}%`, + }, + }, + }, + ], + }); + return; + case ViewFilterOperand.DoesNotContain: + whereClause.push({ + and: [ + { + not: { + [correspondingField.name]: { + firstName: { + ilike: `%${filter.value}%`, + }, + }, + }, + }, + { + not: { + [correspondingField.name]: { + lastName: { + ilike: `%${filter.value}%`, + }, + }, + }, + }, + ], + }); return; default: throw new Error( @@ -100,5 +217,6 @@ export const turnFiltersIntoWhereClauseV2 = ( throw new Error('Unknown filter type'); } }); - return whereClause; + + return { and: whereClause }; }; diff --git a/front/src/modules/views/hooks/internal/useViewFilters.ts b/front/src/modules/views/hooks/internal/useViewFilters.ts index 96a837c52..9dc152ca7 100644 --- a/front/src/modules/views/hooks/internal/useViewFilters.ts +++ b/front/src/modules/views/hooks/internal/useViewFilters.ts @@ -166,7 +166,7 @@ export const useViewFilters = (viewScopeId: string) => { filter.fieldMetadataId === filterToUpsert.fieldMetadataId, ); - if (existingFilterIndex === -1) { + if (existingFilterIndex === -1 && filterToUpsert.value !== '') { filtersDraft.push({ ...filterToUpsert, id: existingSavedFilterId, @@ -174,6 +174,11 @@ export const useViewFilters = (viewScopeId: string) => { return filtersDraft; } + if (filterToUpsert.value === '') { + filtersDraft.splice(existingFilterIndex, 1); + return filtersDraft; + } + filtersDraft[existingFilterIndex] = { ...filterToUpsert, id: existingSavedFilterId, diff --git a/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx index 9b71f1969..76c885cf2 100644 --- a/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx +++ b/front/src/pages/opportunities/constants/opportunityBoardFilterDefinitions.tsx @@ -19,14 +19,12 @@ export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity, + type: 'RELATION', }, { fieldMetadataId: 'pointOfContactId', label: 'Point of contact', iconName: 'IconUser', - type: 'ENTITY', - //entitySelectComponent: , + type: 'RELATION', }, ]; diff --git a/front/src/pages/tasks/tasks-filter-definitions.tsx b/front/src/pages/tasks/tasks-filter-definitions.tsx index 75d637871..27379f175 100644 --- a/front/src/pages/tasks/tasks-filter-definitions.tsx +++ b/front/src/pages/tasks/tasks-filter-definitions.tsx @@ -8,7 +8,7 @@ export const tasksFilterDefinitions: FilterDefinitionByEntity[] = [ fieldMetadataId: 'assigneeId', label: 'Assignee', iconName: 'IconUser', - type: 'ENTITY', + type: 'RELATION', entitySelectComponent: , selectAllLabel: 'All assignees', SelectAllIcon: IconUserCircle,