Fixed bugs in ViewBar filtering (#7608)
- Fixed CSS for SortOrFilter chips - Fixed bug when refreshing with an actor source filter set --------- Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -36,8 +36,7 @@ export const triggerDestroyRecordsOptimisticEffect = ({
|
|||||||
|
|
||||||
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
const rootQueryCachedObjectRecordConnection = rootQueryCachedResponse;
|
||||||
|
|
||||||
const recordIdsToDelete = recordsToDestroy.map(({ id }) => id);
|
const recordIdsToDestroy = recordsToDestroy.map(({ id }) => id);
|
||||||
|
|
||||||
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
const cachedEdges = readField<RecordGqlRefEdge[]>(
|
||||||
'edges',
|
'edges',
|
||||||
rootQueryCachedObjectRecordConnection,
|
rootQueryCachedObjectRecordConnection,
|
||||||
@ -52,7 +51,7 @@ export const triggerDestroyRecordsOptimisticEffect = ({
|
|||||||
cachedEdges?.filter((cachedEdge) => {
|
cachedEdges?.filter((cachedEdge) => {
|
||||||
const nodeId = readField<string>('id', cachedEdge.node);
|
const nodeId = readField<string>('id', cachedEdge.node);
|
||||||
|
|
||||||
return nodeId && !recordIdsToDelete.includes(nodeId);
|
return nodeId && !recordIdsToDestroy.includes(nodeId);
|
||||||
}) || [];
|
}) || [];
|
||||||
|
|
||||||
if (nextCachedEdges.length === cachedEdges?.length)
|
if (nextCachedEdges.length === cachedEdges?.length)
|
||||||
@ -62,7 +61,7 @@ export const triggerDestroyRecordsOptimisticEffect = ({
|
|||||||
...rootQueryCachedObjectRecordConnection,
|
...rootQueryCachedObjectRecordConnection,
|
||||||
edges: nextCachedEdges,
|
edges: nextCachedEdges,
|
||||||
totalCount: isDefined(totalCount)
|
totalCount: isDefined(totalCount)
|
||||||
? totalCount - recordIdsToDelete.length
|
? totalCount - recordIdsToDestroy.length
|
||||||
: undefined,
|
: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { StyledInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect';
|
|
||||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||||
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState';
|
||||||
import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState';
|
import { objectFilterDropdownFirstLevelFilterDefinitionComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFirstLevelFilterDefinitionComponentState';
|
||||||
|
import { objectFilterDropdownIsSelectingCompositeFieldComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownIsSelectingCompositeFieldComponentState';
|
||||||
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
import { objectFilterDropdownSubMenuFieldTypeComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSubMenuFieldTypeComponentState';
|
||||||
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
import { FilterDefinition } from '@/object-record/object-filter-dropdown/types/FilterDefinition';
|
||||||
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
import { getCompositeSubFieldLabel } from '@/object-record/object-filter-dropdown/utils/getCompositeSubFieldLabel';
|
||||||
@ -16,7 +16,7 @@ import { useState } from 'react';
|
|||||||
import { IconApps, IconChevronLeft, isDefined, useIcons } from 'twenty-ui';
|
import { IconApps, IconChevronLeft, isDefined, useIcons } from 'twenty-ui';
|
||||||
|
|
||||||
export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
||||||
const [searchText, setSearchText] = useState('');
|
const [searchText] = useState('');
|
||||||
|
|
||||||
const { getIcon } = useIcons();
|
const { getIcon } = useIcons();
|
||||||
|
|
||||||
@ -31,6 +31,11 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
objectFilterDropdownFilterIsSelectedComponentState,
|
objectFilterDropdownFilterIsSelectedComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [, setObjectFilterDropdownIsSelectingCompositeField] =
|
||||||
|
useRecoilComponentStateV2(
|
||||||
|
objectFilterDropdownIsSelectingCompositeFieldComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
objectFilterDropdownSubMenuFieldType,
|
objectFilterDropdownSubMenuFieldType,
|
||||||
setObjectFilterDropdownSubMenuFieldType,
|
setObjectFilterDropdownSubMenuFieldType,
|
||||||
@ -62,6 +67,8 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
setFilterDefinitionUsedInDropdown(null);
|
setFilterDefinitionUsedInDropdown(null);
|
||||||
setObjectFilterDropdownSubMenuFieldType(null);
|
setObjectFilterDropdownSubMenuFieldType(null);
|
||||||
setObjectFilterDropdownFirstLevelFilterDefinition(null);
|
setObjectFilterDropdownFirstLevelFilterDefinition(null);
|
||||||
|
setObjectFilterDropdownIsSelectingCompositeField(false);
|
||||||
|
setObjectFilterDropdownFilterIsSelected(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -87,14 +94,14 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
>
|
>
|
||||||
{getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)}
|
{getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)}
|
||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
<StyledInput
|
{/* <StyledInput
|
||||||
value={searchText}
|
value={searchText}
|
||||||
autoFocus
|
autoFocus
|
||||||
placeholder="Search fields"
|
placeholder="Search fields"
|
||||||
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
onChange={(event: React.ChangeEvent<HTMLInputElement>) =>
|
||||||
setSearchText(event.target.value)
|
setSearchText(event.target.value)
|
||||||
}
|
}
|
||||||
/>
|
/> */}
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={`select-filter-${-1}`}
|
key={`select-filter-${-1}`}
|
||||||
@ -105,31 +112,33 @@ export const ObjectFilterDropdownFilterSelectCompositeFieldSubMenu = () => {
|
|||||||
LeftIcon={IconApps}
|
LeftIcon={IconApps}
|
||||||
text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`}
|
text={`Any ${getFilterableFieldTypeLabel(objectFilterDropdownSubMenuFieldType)} field`}
|
||||||
/>
|
/>
|
||||||
{options.map((subFieldName, index) => (
|
{/* TODO: fix this with a backend field on ViewFilter for composite field filter */}
|
||||||
<MenuItem
|
{objectFilterDropdownFirstLevelFilterDefinition.type === 'ACTOR' &&
|
||||||
key={`select-filter-${index}`}
|
options.map((subFieldName, index) => (
|
||||||
testId={`select-filter-${index}`}
|
<MenuItem
|
||||||
onClick={() => {
|
key={`select-filter-${index}`}
|
||||||
if (isDefined(objectFilterDropdownFirstLevelFilterDefinition)) {
|
testId={`select-filter-${index}`}
|
||||||
handleSelectFilter({
|
onClick={() => {
|
||||||
...objectFilterDropdownFirstLevelFilterDefinition,
|
if (isDefined(objectFilterDropdownFirstLevelFilterDefinition)) {
|
||||||
label: getCompositeSubFieldLabel(
|
handleSelectFilter({
|
||||||
objectFilterDropdownSubMenuFieldType,
|
...objectFilterDropdownFirstLevelFilterDefinition,
|
||||||
subFieldName,
|
label: getCompositeSubFieldLabel(
|
||||||
),
|
objectFilterDropdownSubMenuFieldType,
|
||||||
compositeFieldName: subFieldName,
|
subFieldName,
|
||||||
});
|
),
|
||||||
}
|
compositeFieldName: subFieldName,
|
||||||
}}
|
});
|
||||||
text={getCompositeSubFieldLabel(
|
}
|
||||||
objectFilterDropdownSubMenuFieldType,
|
}}
|
||||||
subFieldName,
|
text={getCompositeSubFieldLabel(
|
||||||
)}
|
objectFilterDropdownSubMenuFieldType,
|
||||||
LeftIcon={getIcon(
|
subFieldName,
|
||||||
objectFilterDropdownFirstLevelFilterDefinition?.iconName,
|
)}
|
||||||
)}
|
LeftIcon={getIcon(
|
||||||
/>
|
objectFilterDropdownFirstLevelFilterDefinition?.iconName,
|
||||||
))}
|
)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</DropdownMenuItemsContainer>
|
</DropdownMenuItemsContainer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import {
|
|||||||
convertRatingToRatingValue,
|
convertRatingToRatingValue,
|
||||||
} from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
|
} from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownRatingInput';
|
||||||
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
|
||||||
import { isActorSourceCompositeFilter } from '@/object-record/object-filter-dropdown/utils/isActorSourceCompositeFilter';
|
|
||||||
import { applyEmptyFilters } from '@/object-record/record-filter/utils/applyEmptyFilters';
|
import { applyEmptyFilters } from '@/object-record/record-filter/utils/applyEmptyFilters';
|
||||||
import { resolveFilterValue } from '@/views/utils/view-filter-value/resolveFilterValue';
|
import { resolveFilterValue } from '@/views/utils/view-filter-value/resolveFilterValue';
|
||||||
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
||||||
@ -677,81 +676,83 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'ACTOR':
|
// TODO: fix this with a new composite field in ViewFilter entity
|
||||||
if (isActorSourceCompositeFilter(rawUIFilter.definition)) {
|
case 'ACTOR': {
|
||||||
const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[];
|
switch (rawUIFilter.operand) {
|
||||||
|
case ViewFilterOperand.Is: {
|
||||||
|
const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[];
|
||||||
|
|
||||||
switch (rawUIFilter.operand) {
|
objectRecordFilters.push({
|
||||||
case ViewFilterOperand.Is:
|
[correspondingField.name]: {
|
||||||
|
source: {
|
||||||
|
in: parsedRecordIds,
|
||||||
|
} as RelationFilter,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ViewFilterOperand.IsNot: {
|
||||||
|
const parsedRecordIds = JSON.parse(rawUIFilter.value) as string[];
|
||||||
|
|
||||||
|
if (parsedRecordIds.length > 0) {
|
||||||
objectRecordFilters.push({
|
objectRecordFilters.push({
|
||||||
[correspondingField.name]: {
|
not: {
|
||||||
source: {
|
[correspondingField.name]: {
|
||||||
in: parsedRecordIds,
|
source: {
|
||||||
} as RelationFilter,
|
in: parsedRecordIds,
|
||||||
|
} as RelationFilter,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ViewFilterOperand.IsNot:
|
|
||||||
if (parsedRecordIds.length > 0) {
|
|
||||||
objectRecordFilters.push({
|
|
||||||
not: {
|
|
||||||
[correspondingField.name]: {
|
|
||||||
source: {
|
|
||||||
in: parsedRecordIds,
|
|
||||||
} as RelationFilter,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
case ViewFilterOperand.Contains:
|
||||||
switch (rawUIFilter.operand) {
|
objectRecordFilters.push({
|
||||||
case ViewFilterOperand.Contains:
|
or: [
|
||||||
objectRecordFilters.push({
|
{
|
||||||
or: [
|
[correspondingField.name]: {
|
||||||
{
|
name: {
|
||||||
|
ilike: `%${rawUIFilter.value}%`,
|
||||||
|
},
|
||||||
|
} as ActorFilter,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case ViewFilterOperand.DoesNotContain:
|
||||||
|
objectRecordFilters.push({
|
||||||
|
and: [
|
||||||
|
{
|
||||||
|
not: {
|
||||||
[correspondingField.name]: {
|
[correspondingField.name]: {
|
||||||
name: {
|
name: {
|
||||||
ilike: `%${rawUIFilter.value}%`,
|
ilike: `%${rawUIFilter.value}%`,
|
||||||
},
|
},
|
||||||
} as ActorFilter,
|
} as ActorFilter,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
});
|
],
|
||||||
break;
|
});
|
||||||
case ViewFilterOperand.DoesNotContain:
|
break;
|
||||||
objectRecordFilters.push({
|
case ViewFilterOperand.IsEmpty:
|
||||||
and: [
|
case ViewFilterOperand.IsNotEmpty:
|
||||||
{
|
applyEmptyFilters(
|
||||||
not: {
|
rawUIFilter.operand,
|
||||||
[correspondingField.name]: {
|
correspondingField,
|
||||||
name: {
|
objectRecordFilters,
|
||||||
ilike: `%${rawUIFilter.value}%`,
|
rawUIFilter.definition,
|
||||||
},
|
);
|
||||||
} as ActorFilter,
|
break;
|
||||||
},
|
|
||||||
},
|
default:
|
||||||
],
|
throw new Error(
|
||||||
});
|
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.label} filter`,
|
||||||
break;
|
);
|
||||||
case ViewFilterOperand.IsEmpty:
|
|
||||||
case ViewFilterOperand.IsNotEmpty:
|
|
||||||
applyEmptyFilters(
|
|
||||||
rawUIFilter.operand,
|
|
||||||
correspondingField,
|
|
||||||
objectRecordFilters,
|
|
||||||
rawUIFilter.definition,
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unknown operand ${rawUIFilter.operand} for ${rawUIFilter.definition.label} filter`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case 'EMAILS':
|
case 'EMAILS':
|
||||||
switch (rawUIFilter.operand) {
|
switch (rawUIFilter.operand) {
|
||||||
case ViewFilterOperand.Contains:
|
case ViewFilterOperand.Contains:
|
||||||
|
|||||||
@ -39,9 +39,11 @@ const StyledChip = styled.div<{ variant: SortOrFitlerChipVariant }>`
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: ${({ theme }) => theme.font.size.sm};
|
font-size: ${({ theme }) => theme.font.size.sm};
|
||||||
font-weight: ${({ theme }) => theme.font.weight.medium};
|
font-weight: ${({ theme }) => theme.font.weight.medium};
|
||||||
padding: ${({ theme }) => theme.spacing(1) + ' ' + theme.spacing(2)};
|
padding: ${({ theme }) => theme.spacing(0.5) + ' ' + theme.spacing(2)};
|
||||||
user-select: none;
|
user-select: none;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
||||||
|
max-height: ${({ theme }) => theme.spacing(4.5)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledIcon = styled.div`
|
const StyledIcon = styled.div`
|
||||||
|
|||||||
@ -50,7 +50,6 @@ const StyledChipcontainer = styled.div`
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
padding-top: ${({ theme }) => theme.spacing(1)};
|
padding-top: ${({ theme }) => theme.spacing(1)};
|
||||||
padding-bottom: ${({ theme }) => theme.spacing(0.5)};
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user