mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-09 02:11:55 +03:00
<img width="956" alt="filter-icp-true" src="https://github.com/user-attachments/assets/fc5fe18d-c7b6-463d-9ce7-8e5facb7352f"> Link to issue: https://github.com/twentyhq/twenty/issues/7190
This commit is contained in:
parent
fd8e0d04a2
commit
bad7ad464b
@ -61,7 +61,7 @@ describe('useColumnDefinitionsFromFieldMetadata', () => {
|
||||
result.current;
|
||||
|
||||
expect(columnDefinitions.length).toBe(21);
|
||||
expect(filterDefinitions.length).toBe(15);
|
||||
expect(filterDefinitions.length).toBe(17);
|
||||
expect(sortDefinitions.length).toBe(14);
|
||||
});
|
||||
});
|
||||
|
@ -25,6 +25,7 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({
|
||||
|
||||
if (
|
||||
![
|
||||
FieldMetadataType.Boolean,
|
||||
FieldMetadataType.DateTime,
|
||||
FieldMetadataType.Date,
|
||||
FieldMetadataType.Text,
|
||||
@ -100,6 +101,8 @@ export const getFilterTypeFromFieldType = (fieldType: FieldMetadataType) => {
|
||||
return 'ARRAY';
|
||||
case FieldMetadataType.RawJson:
|
||||
return 'RAW_JSON';
|
||||
case FieldMetadataType.Boolean:
|
||||
return 'BOOLEAN';
|
||||
default:
|
||||
return 'TEXT';
|
||||
}
|
||||
|
@ -0,0 +1,113 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useFilterDropdown';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { BooleanDisplay } from '@/ui/field/display/components/BooleanDisplay';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
|
||||
import { IconCheck } from 'twenty-ui';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
const StyledBooleanSelectContainer = styled.div<{ selected?: boolean }>`
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
padding: ${({ theme }) =>
|
||||
`${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing(2)} ${theme.spacing(1)}`};
|
||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||
|
||||
&:hover {
|
||||
background: ${({ theme }) => theme.background.transparent.light};
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledIconCheckContainer = styled.div`
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export const ObjectFilterDropdownBooleanSelect = () => {
|
||||
const theme = useTheme();
|
||||
const options = [true, false];
|
||||
|
||||
const {
|
||||
filterDefinitionUsedInDropdownState,
|
||||
selectedOperandInDropdownState,
|
||||
selectedFilterState,
|
||||
selectFilter,
|
||||
} = useFilterDropdown();
|
||||
|
||||
const { closeDropdown } = useDropdown();
|
||||
|
||||
const filterDefinitionUsedInDropdown = useRecoilValue(
|
||||
filterDefinitionUsedInDropdownState,
|
||||
);
|
||||
const selectedOperandInDropdown = useRecoilValue(
|
||||
selectedOperandInDropdownState,
|
||||
);
|
||||
const selectedFilter = useRecoilValue(selectedFilterState);
|
||||
|
||||
const [selectedValue, setSelectedValue] = useState<boolean | undefined>(
|
||||
selectedFilter?.value === 'true',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedValue(selectedFilter?.value === 'true');
|
||||
}, [selectedFilter?.value]);
|
||||
|
||||
const handleOptionSelect = (value: boolean) => {
|
||||
if (
|
||||
!isDefined(filterDefinitionUsedInDropdown) ||
|
||||
!isDefined(selectedOperandInDropdown)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectFilter({
|
||||
id: selectedFilter?.id ?? v4(),
|
||||
definition: filterDefinitionUsedInDropdown,
|
||||
operand: selectedOperandInDropdown,
|
||||
displayValue: value ? 'True' : 'False',
|
||||
fieldMetadataId: filterDefinitionUsedInDropdown.fieldMetadataId,
|
||||
value: value.toString(),
|
||||
viewFilterGroupId: selectedFilter?.viewFilterGroupId,
|
||||
});
|
||||
|
||||
setSelectedValue(value);
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectableList
|
||||
selectableListId="boolean-select"
|
||||
selectableItemIdArray={options.map((option) => option.toString())}
|
||||
hotkeyScope={RelationPickerHotkeyScope.RelationPicker}
|
||||
onEnter={(itemId) => {
|
||||
handleOptionSelect(itemId === 'true');
|
||||
}}
|
||||
>
|
||||
<DropdownMenuItemsContainer hasMaxHeight>
|
||||
{options.map((option) => (
|
||||
<StyledBooleanSelectContainer
|
||||
key={String(option)}
|
||||
onClick={() => handleOptionSelect(option)}
|
||||
selected={selectedValue === option}
|
||||
>
|
||||
<BooleanDisplay value={option} />
|
||||
{selectedFilter?.value === option.toString() && (
|
||||
<StyledIconCheckContainer>
|
||||
<IconCheck color={theme.grayScale.gray50} size={16} />
|
||||
</StyledIconCheckContainer>
|
||||
)}
|
||||
</StyledBooleanSelectContainer>
|
||||
))}
|
||||
</DropdownMenuItemsContainer>
|
||||
</SelectableList>
|
||||
);
|
||||
};
|
@ -14,6 +14,7 @@ import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownM
|
||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
|
||||
import { ObjectFilterDropdownBooleanSelect } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownBooleanSelect';
|
||||
import { DATE_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/DateFilterTypes';
|
||||
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
|
||||
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
|
||||
@ -96,6 +97,9 @@ export const ObjectFilterDropdownFilterInput = ({
|
||||
<ObjectFilterDropdownOptionSelect />
|
||||
</>
|
||||
)}
|
||||
{filterDefinitionUsedInDropdown.type === 'BOOLEAN' && (
|
||||
<ObjectFilterDropdownBooleanSelect />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
@ -20,4 +20,5 @@ export type FilterableFieldType = PickLiteral<
|
||||
| 'ACTOR'
|
||||
| 'ARRAY'
|
||||
| 'RAW_JSON'
|
||||
| 'BOOLEAN'
|
||||
>;
|
||||
|
@ -87,6 +87,8 @@ export const getOperandsForFilterDefinition = (
|
||||
ViewFilterOperand.DoesNotContain,
|
||||
...emptyOperands,
|
||||
];
|
||||
case 'BOOLEAN':
|
||||
return [ViewFilterOperand.Is];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
ActorFilter,
|
||||
AddressFilter,
|
||||
ArrayFilter,
|
||||
BooleanFilter,
|
||||
CurrencyFilter,
|
||||
DateFilter,
|
||||
EmailsFilter,
|
||||
@ -855,6 +856,13 @@ const computeFilterRecordGqlOperationFilter = (
|
||||
);
|
||||
}
|
||||
}
|
||||
case 'BOOLEAN': {
|
||||
return {
|
||||
[correspondingField.name]: {
|
||||
eq: filter.value === 'true',
|
||||
} as BooleanFilter,
|
||||
};
|
||||
}
|
||||
default:
|
||||
throw new Error('Unknown filter type');
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
|
||||
export const resolveBooleanViewFilterValue = (
|
||||
viewFilter: Pick<ViewFilter, 'value'>,
|
||||
) => {
|
||||
return viewFilter.value === 'true';
|
||||
};
|
@ -7,6 +7,7 @@ import {
|
||||
resolveDateViewFilterValue,
|
||||
ResolvedDateViewFilterValue,
|
||||
} from './resolveDateViewFilterValue';
|
||||
import { resolveBooleanViewFilterValue } from '@/views/view-filter-value/utils/resolveBooleanViewFilterValue';
|
||||
|
||||
type ResolvedFilterValue<
|
||||
T extends FilterableFieldType,
|
||||
@ -17,7 +18,9 @@ type ResolvedFilterValue<
|
||||
? ReturnType<typeof resolveNumberViewFilterValue>
|
||||
: T extends 'SELECT' | 'MULTI_SELECT'
|
||||
? string[]
|
||||
: string;
|
||||
: T extends 'BOOLEAN'
|
||||
? boolean
|
||||
: string;
|
||||
|
||||
type PartialFilter<
|
||||
T extends FilterableFieldType,
|
||||
@ -42,6 +45,8 @@ export const resolveFilterValue = <
|
||||
case 'SELECT':
|
||||
case 'MULTI_SELECT':
|
||||
return resolveSelectViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
case 'BOOLEAN':
|
||||
return resolveBooleanViewFilterValue(filter) as ResolvedFilterValue<T, O>;
|
||||
default:
|
||||
return filter.value as ResolvedFilterValue<T, O>;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user