Enrich filters with all types (#2653)

This commit is contained in:
Charles Bochet
2023-11-22 17:23:10 +01:00
committed by GitHub
parent 0fd823af21
commit 8f12aea64a
20 changed files with 315 additions and 111 deletions

View File

@ -20,7 +20,7 @@ export const KeyboardShortcutMenu = () => {
isKeyboardShortcutMenuOpenedState, isKeyboardShortcutMenuOpenedState,
); );
useScopedHotkeys( useScopedHotkeys(
'shift+?,meta+?,esc', 'shift+?,meta+?',
() => { () => {
toggleKeyboardShortcutMenu(); toggleKeyboardShortcutMenu();
}, },

View File

@ -12,14 +12,29 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({
if ( if (
![ ![
FieldMetadataType.DateTime, FieldMetadataType.DateTime,
FieldMetadataType.Number,
FieldMetadataType.Currency,
FieldMetadataType.Text, FieldMetadataType.Text,
].includes(field.type) || FieldMetadataType.Email,
field.name === 'probability' FieldMetadataType.Number,
FieldMetadataType.Link,
FieldMetadataType.FullName,
FieldMetadataType.Relation,
FieldMetadataType.Currency,
].includes(field.type)
) { ) {
return acc; 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 })]; return [...acc, formatFieldMetadataItemAsFilterDefinition({ field })];
}, [] as FilterDefinition[]); }, [] as FilterDefinition[]);
@ -34,9 +49,19 @@ const formatFieldMetadataItemAsFilterDefinition = ({
type: type:
field.type === FieldMetadataType.DateTime field.type === FieldMetadataType.DateTime
? 'DATE_TIME' ? 'DATE_TIME'
: field.type === FieldMetadataType.Link
? 'LINK'
: field.type === FieldMetadataType.FullName
? 'FULL_NAME'
: field.type === FieldMetadataType.Number : field.type === FieldMetadataType.Number
? 'NUMBER' ? 'NUMBER'
: field.type === FieldMetadataType.Currency : field.type === FieldMetadataType.Currency
? 'CURRENCY' ? 'CURRENCY'
: field.type === FieldMetadataType.Email
? 'TEXT'
: field.type === FieldMetadataType.Phone
? 'TEXT'
: field.type === FieldMetadataType.Relation
? 'RELATION'
: 'TEXT', : 'TEXT',
}); });

View File

@ -4,16 +4,16 @@ import { useQuery } from '@apollo/client';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { IconUserCircle } from '@/ui/display/icon'; 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 { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState'; import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; 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 { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata'; import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
export type RelationPickerProps = { export type RelationPickerProps = {
recordId: string; recordId?: string;
onSubmit: (newUser: EntityForSelect | null) => void; onSubmit: (newUser: EntityForSelect | null) => void;
onCancel?: () => void; onCancel?: () => void;
width?: number; width?: number;
@ -43,9 +43,9 @@ export const RelationPicker = ({
const useFindManyQuery = (options: any) => useQuery(findManyQuery, options); const useFindManyQuery = (options: any) => useQuery(findManyQuery, options);
const { identifiersMapper, searchQuery } = useRelationField(); const { identifiersMapper, searchQuery } = useRelationPicker();
const workspaceMembers = useFilteredSearchEntityQuery({ const records = useFilteredSearchEntityQuery({
queryHook: useFindManyQuery, queryHook: useFindManyQuery,
filters: [ filters: [
{ {
@ -74,11 +74,11 @@ export const RelationPicker = ({
<SingleEntitySelect <SingleEntitySelect
EmptyIcon={IconUserCircle} EmptyIcon={IconUserCircle}
emptyLabel="No Owner" emptyLabel="No Owner"
entitiesToSelect={workspaceMembers.entitiesToSelect} entitiesToSelect={records.entitiesToSelect}
loading={workspaceMembers.loading} loading={records.loading}
onCancel={onCancel} onCancel={onCancel}
onEntitySelected={handleEntitySelected} onEntitySelected={handleEntitySelected}
selectedEntity={workspaceMembers.selectedEntities[0]} selectedEntity={records.selectedEntities[0]}
width={width} width={width}
/> />
); );

View File

@ -1,11 +1,12 @@
import { EntityChip } from '@/ui/display/chip/components/EntityChip'; import { EntityChip } from '@/ui/display/chip/components/EntityChip';
import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker';
import { useRelationField } from '../../hooks/useRelationField'; import { useRelationField } from '../../hooks/useRelationField';
export const RelationFieldDisplay = () => { export const RelationFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationField(); const { fieldValue, fieldDefinition } = useRelationField();
const { identifiersMapper } = useRelationField(); const { identifiersMapper } = useRelationPicker();
if (!fieldValue || !fieldDefinition || !identifiersMapper) { if (!fieldValue || !fieldDefinition || !identifiersMapper) {
return <></>; return <></>;

View File

@ -1,8 +1,6 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { useRelationPicker } from '@/ui/input/components/internal/relation-picker/hooks/useRelationPicker';
import { FieldContext } from '../../contexts/FieldContext'; import { FieldContext } from '../../contexts/FieldContext';
import { useFieldInitialValue } from '../../hooks/useFieldInitialValue'; import { useFieldInitialValue } from '../../hooks/useFieldInitialValue';
import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector'; import { entityFieldsFamilySelector } from '../../states/selectors/entityFieldsFamilySelector';
@ -32,15 +30,11 @@ export const useRelationField = () => {
const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue; const initialValue = fieldInitialValue?.isEmpty ? null : fieldValue;
const { identifiersMapper, searchQuery } = useRelationPicker();
return { return {
fieldDefinition, fieldDefinition,
fieldValue, fieldValue,
initialValue, initialValue,
initialSearchValue, initialSearchValue,
setFieldValue, setFieldValue,
searchQuery,
identifiersMapper,
}; };
}; };

View File

@ -1,7 +1,7 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import styled from '@emotion/styled'; 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 { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { usePersistField } from '../../../hooks/usePersistField'; import { usePersistField } from '../../../hooks/usePersistField';

View File

@ -1,5 +1,6 @@
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownScope } from '@/ui/layout/dropdown/scopes/DropdownScope'; 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 { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId'; import { ObjectFilterDropdownId } from '../constants/ObjectFilterDropdownId';
@ -14,9 +15,12 @@ type MultipleFiltersDropdownButtonProps = {
export const MultipleFiltersDropdownButton = ({ export const MultipleFiltersDropdownButton = ({
hotkeyScope, hotkeyScope,
}: MultipleFiltersDropdownButtonProps) => { }: MultipleFiltersDropdownButtonProps) => {
const { resetFilter } = useFilter();
return ( return (
<DropdownScope dropdownScopeId={ObjectFilterDropdownId}> <DropdownScope dropdownScopeId={ObjectFilterDropdownId}>
<Dropdown <Dropdown
onClose={resetFilter}
clickableComponent={<MultipleFiltersButton />} clickableComponent={<MultipleFiltersButton />}
dropdownComponents={<MultipleFiltersDropdownContent />} dropdownComponents={<MultipleFiltersDropdownContent />}
dropdownHotkeyScope={hotkeyScope} dropdownHotkeyScope={hotkeyScope}

View File

@ -30,19 +30,19 @@ export const MultipleFiltersDropdownContent = () => {
<> <>
<ObjectFilterDropdownOperandButton /> <ObjectFilterDropdownOperandButton />
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{filterDefinitionUsedInDropdown.type === 'TEXT' && ( {['TEXT', 'EMAIL', 'PHONE', 'FULL_NAME', 'LINK'].includes(
<ObjectFilterDropdownTextSearchInput /> filterDefinitionUsedInDropdown.type,
)} ) && <ObjectFilterDropdownTextSearchInput />}
{['NUMBER', 'CURRENCY'].includes( {['NUMBER', 'CURRENCY'].includes(
filterDefinitionUsedInDropdown.type, filterDefinitionUsedInDropdown.type,
) && <ObjectFilterDropdownNumberSearchInput />} ) && <ObjectFilterDropdownNumberSearchInput />}
{filterDefinitionUsedInDropdown.type === 'DATE_TIME' && ( {filterDefinitionUsedInDropdown.type === 'DATE_TIME' && (
<ObjectFilterDropdownDateSearchInput /> <ObjectFilterDropdownDateSearchInput />
)} )}
{filterDefinitionUsedInDropdown.type === 'ENTITY' && ( {filterDefinitionUsedInDropdown.type === 'RELATION' && (
<ObjectFilterDropdownEntitySearchInput /> <ObjectFilterDropdownEntitySearchInput />
)} )}
{filterDefinitionUsedInDropdown.type === 'ENTITY' && ( {filterDefinitionUsedInDropdown.type === 'RELATION' && (
<ObjectFilterDropdownEntitySelect /> <ObjectFilterDropdownEntitySelect />
)} )}
</> </>

View File

@ -16,7 +16,7 @@ export const ObjectFilterDropdownButton = ({
const hasOnlyOneEntityFilter = const hasOnlyOneEntityFilter =
availableFilterDefinitions.length === 1 && availableFilterDefinitions.length === 1 &&
availableFilterDefinitions[0].type === 'ENTITY'; availableFilterDefinitions[0].type === 'RELATION';
if (!availableFilterDefinitions.length) { if (!availableFilterDefinitions.length) {
return <></>; return <></>;

View File

@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect'; import { EntitiesForMultipleEntitySelect } from '@/ui/input/relation-picker/components/MultipleEntitySelect';
import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase'; import { SingleEntitySelectBase } from '@/ui/input/relation-picker/components/SingleEntitySelectBase';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { useFilter } from '../hooks/useFilter'; import { useFilter } from '../hooks/useFilter';
@ -21,9 +22,11 @@ export const ObjectFilterDropdownEntitySearchSelect = ({
selectFilter, selectFilter,
} = useFilter(); } = useFilter();
const { closeDropdown } = useDropdown();
const [isAllEntitySelected, setIsAllEntitySelected] = useState(false); const [isAllEntitySelected, setIsAllEntitySelected] = useState(false);
const handleUserSelected = ( const handleRecordSelected = (
selectedEntity: EntityForSelect | null | undefined, selectedEntity: EntityForSelect | null | undefined,
) => { ) => {
if ( if (
@ -39,6 +42,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({
} }
setObjectFilterDropdownSelectedEntityId(selectedEntity.id); setObjectFilterDropdownSelectedEntityId(selectedEntity.id);
closeDropdown();
selectFilter?.({ selectFilter?.({
displayValue: selectedEntity.name, displayValue: selectedEntity.name,
@ -69,6 +73,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({
setIsAllEntitySelected(true); setIsAllEntitySelected(true);
setObjectFilterDropdownSelectedEntityId(null); setObjectFilterDropdownSelectedEntityId(null);
closeDropdown();
selectFilter?.({ selectFilter?.({
displayValue: filterDefinitionUsedInDropdown.selectAllLabel, displayValue: filterDefinitionUsedInDropdown.selectAllLabel,
@ -100,7 +105,7 @@ export const ObjectFilterDropdownEntitySearchSelect = ({
entitiesToSelect={entitiesForSelect.entitiesToSelect} entitiesToSelect={entitiesForSelect.entitiesToSelect}
selectedEntity={entitiesForSelect.selectedEntities[0]} selectedEntity={entitiesForSelect.selectedEntities[0]}
loading={entitiesForSelect.loading} loading={entitiesForSelect.loading}
onEntitySelected={handleUserSelected} onEntitySelected={handleRecordSelected}
SelectAllIcon={filterDefinitionUsedInDropdown?.SelectAllIcon} SelectAllIcon={filterDefinitionUsedInDropdown?.SelectAllIcon}
selectAllLabel={filterDefinitionUsedInDropdown?.selectAllLabel} selectAllLabel={filterDefinitionUsedInDropdown?.selectAllLabel}
isAllEntitySelected={isAllEntitySelected} isAllEntitySelected={isAllEntitySelected}

View File

@ -1,21 +1,52 @@
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { useQuery } from '@apollo/client';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
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'; import { useFilter } from '../hooks/useFilter';
export const ObjectFilterDropdownEntitySelect = () => { 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 null;
} }
return ( return (
<> <>
<DropdownMenuSeparator /> <ObjectFilterDropdownEntitySearchSelect
<RecoilScope> entitiesForSelect={filteredSearchEntityResults}
{filterDefinitionUsedInDropdown.entitySelectComponent} />
</RecoilScope>
</> </>
); );
}; };

View File

@ -21,27 +21,29 @@ export const ObjectFilterDropdownFilterSelect = () => {
return ( return (
<DropdownMenuItemsContainer> <DropdownMenuItemsContainer>
{availableFilterDefinitions.map((availableFilterDefinition, index) => ( {[...availableFilterDefinitions]
<MenuItem .sort((a, b) => a.label.localeCompare(b.label))
key={`select-filter-${index}`} .map((availableFilterDefinition, index) => (
testId={`select-filter-${index}`} <MenuItem
onClick={() => { key={`select-filter-${index}`}
setFilterDefinitionUsedInDropdown(availableFilterDefinition); testId={`select-filter-${index}`}
onClick={() => {
setFilterDefinitionUsedInDropdown(availableFilterDefinition);
if (availableFilterDefinition.type === 'ENTITY') { if (availableFilterDefinition.type === 'RELATION') {
setHotkeyScope(RelationPickerHotkeyScope.RelationPicker); setHotkeyScope(RelationPickerHotkeyScope.RelationPicker);
} }
setSelectedOperandInDropdown( setSelectedOperandInDropdown(
getOperandsForFilterType(availableFilterDefinition.type)?.[0], getOperandsForFilterType(availableFilterDefinition.type)?.[0],
); );
setObjectFilterDropdownSearchInput(''); setObjectFilterDropdownSearchInput('');
}} }}
LeftIcon={icons[availableFilterDefinition.iconName]} LeftIcon={icons[availableFilterDefinition.iconName]}
text={availableFilterDefinition.label} text={availableFilterDefinition.label}
/> />
))} ))}
</DropdownMenuItemsContainer> </DropdownMenuItemsContainer>
); );
}; };

View File

@ -25,6 +25,7 @@ export const SingleEntityObjectFilterDropdownButton = ({
selectedFilter, selectedFilter,
setFilterDefinitionUsedInDropdown, setFilterDefinitionUsedInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
resetFilter,
} = useFilter(); } = useFilter();
const availableFilter = availableFilterDefinitions[0]; const availableFilter = availableFilterDefinitions[0];

View File

@ -48,6 +48,18 @@ export const useFilter = (props?: UseFilterProps) => {
[setSelectedFilter, onFilterSelect], [setSelectedFilter, onFilterSelect],
); );
const resetFilter = useCallback(() => {
setObjectFilterDropdownSearchInput('');
setObjectFilterDropdownSelectedEntityId(null);
setSelectedFilter(undefined);
setSelectedOperandInDropdown(null);
}, [
setObjectFilterDropdownSearchInput,
setObjectFilterDropdownSelectedEntityId,
setSelectedFilter,
setSelectedOperandInDropdown,
]);
return { return {
scopeId, scopeId,
availableFilterDefinitions, availableFilterDefinitions,
@ -67,5 +79,6 @@ export const useFilter = (props?: UseFilterProps) => {
selectedOperandInDropdown, selectedOperandInDropdown,
setSelectedOperandInDropdown, setSelectedOperandInDropdown,
selectFilter, selectFilter,
resetFilter,
}; };
}; };

View File

@ -1,6 +1,10 @@
export type FilterType = export type FilterType =
| 'TEXT' | 'TEXT'
| 'PHONE'
| 'EMAIL'
| 'DATE_TIME' | 'DATE_TIME'
| 'ENTITY'
| 'NUMBER' | 'NUMBER'
| 'CURRENCY'; | 'CURRENCY'
| 'FULL_NAME'
| 'LINK'
| 'RELATION';

View File

@ -7,12 +7,15 @@ export const getOperandsForFilterType = (
): ViewFilterOperand[] => { ): ViewFilterOperand[] => {
switch (filterType) { switch (filterType) {
case 'TEXT': case 'TEXT':
case 'EMAIL':
case 'FULL_NAME':
case 'LINK':
return [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain]; return [ViewFilterOperand.Contains, ViewFilterOperand.DoesNotContain];
case 'CURRENCY': case 'CURRENCY':
case 'NUMBER': case 'NUMBER':
case 'DATE_TIME': case 'DATE_TIME':
return [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan]; return [ViewFilterOperand.GreaterThan, ViewFilterOperand.LessThan];
case 'ENTITY': case 'RELATION':
return [ViewFilterOperand.Is, ViewFilterOperand.IsNot]; return [ViewFilterOperand.Is, ViewFilterOperand.IsNot];
default: default:
return []; return [];

View File

@ -13,7 +13,7 @@ export const turnFiltersIntoWhereClauseV2 = (
filters: FilterToTurnIntoWhereClause[], filters: FilterToTurnIntoWhereClause[],
fields: Pick<Field, 'id' | 'name'>[], fields: Pick<Field, 'id' | 'name'>[],
) => { ) => {
const whereClause: Record<string, any> = {}; const whereClause: any[] = [];
filters.forEach((filter) => { filters.forEach((filter) => {
const correspondingField = fields.find( const correspondingField = fields.find(
@ -26,53 +26,25 @@ export const turnFiltersIntoWhereClauseV2 = (
} }
switch (filter.definition.type) { switch (filter.definition.type) {
case 'EMAIL':
case 'PHONE':
case 'TEXT': case 'TEXT':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.Contains: case ViewFilterOperand.Contains:
whereClause[correspondingField.name] = { whereClause.push({
eq: filter.value, [correspondingField.name]: {
}; ilike: `%${filter.value}%`,
},
});
return; return;
case ViewFilterOperand.DoesNotContain: case ViewFilterOperand.DoesNotContain:
whereClause[correspondingField.name] = { whereClause.push({
not: { 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; return;
default: default:
throw new Error( throw new Error(
@ -82,14 +54,159 @@ export const turnFiltersIntoWhereClauseV2 = (
case 'DATE_TIME': case 'DATE_TIME':
switch (filter.operand) { switch (filter.operand) {
case ViewFilterOperand.GreaterThan: case ViewFilterOperand.GreaterThan:
whereClause[correspondingField.name] = { whereClause.push({
gte: filter.value, [correspondingField.name]: {
}; gte: filter.value,
},
});
return; return;
case ViewFilterOperand.LessThan: case ViewFilterOperand.LessThan:
whereClause[correspondingField.name] = { whereClause.push({
lte: filter.value, [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; return;
default: default:
throw new Error( throw new Error(
@ -100,5 +217,6 @@ export const turnFiltersIntoWhereClauseV2 = (
throw new Error('Unknown filter type'); throw new Error('Unknown filter type');
} }
}); });
return whereClause;
return { and: whereClause };
}; };

View File

@ -166,7 +166,7 @@ export const useViewFilters = (viewScopeId: string) => {
filter.fieldMetadataId === filterToUpsert.fieldMetadataId, filter.fieldMetadataId === filterToUpsert.fieldMetadataId,
); );
if (existingFilterIndex === -1) { if (existingFilterIndex === -1 && filterToUpsert.value !== '') {
filtersDraft.push({ filtersDraft.push({
...filterToUpsert, ...filterToUpsert,
id: existingSavedFilterId, id: existingSavedFilterId,
@ -174,6 +174,11 @@ export const useViewFilters = (viewScopeId: string) => {
return filtersDraft; return filtersDraft;
} }
if (filterToUpsert.value === '') {
filtersDraft.splice(existingFilterIndex, 1);
return filtersDraft;
}
filtersDraft[existingFilterIndex] = { filtersDraft[existingFilterIndex] = {
...filterToUpsert, ...filterToUpsert,
id: existingSavedFilterId, id: existingSavedFilterId,

View File

@ -19,14 +19,12 @@ export const opportunityBoardFilterDefinitions: FilterDefinitionByEntity<Opportu
fieldMetadataId: 'companyId', fieldMetadataId: 'companyId',
label: 'Company', label: 'Company',
iconName: 'IconBuildingSkyscraper', iconName: 'IconBuildingSkyscraper',
type: 'ENTITY', type: 'RELATION',
// entitySelectComponent: <FilterDropdownCompanySearchSelect />,
}, },
{ {
fieldMetadataId: 'pointOfContactId', fieldMetadataId: 'pointOfContactId',
label: 'Point of contact', label: 'Point of contact',
iconName: 'IconUser', iconName: 'IconUser',
type: 'ENTITY', type: 'RELATION',
//entitySelectComponent: <FilterDropdownPeopleSearchSelect />,
}, },
]; ];

View File

@ -8,7 +8,7 @@ export const tasksFilterDefinitions: FilterDefinitionByEntity<Activity>[] = [
fieldMetadataId: 'assigneeId', fieldMetadataId: 'assigneeId',
label: 'Assignee', label: 'Assignee',
iconName: 'IconUser', iconName: 'IconUser',
type: 'ENTITY', type: 'RELATION',
entitySelectComponent: <FilterDropdownUserSearchSelect />, entitySelectComponent: <FilterDropdownUserSearchSelect />,
selectAllLabel: 'All assignees', selectAllLabel: 'All assignees',
SelectAllIcon: IconUserCircle, SelectAllIcon: IconUserCircle,