diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx index 64a0c04f6..018d8cf3b 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect.tsx @@ -1,3 +1,4 @@ +import { ActionButton } from '@/action-menu/actions/display/components/ActionButton'; import { getFilterTypeFromFieldType } from '@/object-metadata/utils/formatFieldMetadataItemsAsFilterDefinitions'; import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups'; import { useDefaultFieldMetadataItemForFilter } from '@/object-record/advanced-filter/hooks/useDefaultFieldMetadataItemForFilter'; @@ -17,7 +18,6 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly'; import { isDefined } from 'twenty-shared/utils'; import { IconLibraryPlus, IconPlus } from 'twenty-ui/display'; -import { LightButton } from 'twenty-ui/input'; import { MenuItem } from 'twenty-ui/navigation'; import { v4 } from 'uuid'; @@ -137,9 +137,13 @@ export const AdvancedFilterAddFilterRuleSelect = ({ if (!isFilterRuleGroupOptionVisible) { return ( - ); @@ -149,7 +153,14 @@ export const AdvancedFilterAddFilterRuleSelect = ({ + } dropdownComponents={ diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx index d4c997084..237b2edb6 100644 --- a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect.tsx @@ -1,39 +1,23 @@ -import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset'; -import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand'; - +import { AdvancedFilterRecordFilterOperandSelectContent } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent'; import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel'; import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { getRecordFilterOperands } from '@/object-record/record-filter/utils/getRecordFilterOperands'; import { SelectControl } from '@/ui/input/components/SelectControl'; -import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; -import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; -import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; -import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; -import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; -import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import styled from '@emotion/styled'; import { isDefined } from 'twenty-shared/utils'; -import { MenuItem } from 'twenty-ui/navigation'; -const StyledContainer = styled.div<{ width?: string }>` - width: ${({ width }) => width ?? '100px'}; +const StyledContainer = styled.div` + width: 100px; `; type AdvancedFilterRecordFilterOperandSelectProps = { recordFilterId: string; - widthFromProps?: string; }; export const AdvancedFilterRecordFilterOperandSelect = ({ recordFilterId, - widthFromProps, }: AdvancedFilterRecordFilterOperandSelectProps) => { - const dropdownId = `advanced-filter-view-filter-operand-${recordFilterId}`; - const currentRecordFilters = useRecoilComponentValueV2( currentRecordFiltersComponentState, ); @@ -44,17 +28,6 @@ export const AdvancedFilterRecordFilterOperandSelect = ({ const isDisabled = !filter?.fieldMetadataId; - const { closeDropdown } = useDropdown(dropdownId); - - const { applyObjectFilterDropdownOperand } = - useApplyObjectFilterDropdownOperand(); - - const handleOperandChange = (operand: ViewFilterOperand) => { - closeDropdown(); - - applyObjectFilterDropdownOperand(operand); - }; - const filterType = filter?.type; const operandsForFilterType = isDefined(filterType) @@ -64,12 +37,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({ }) : []; - const selectedItemId = useRecoilComponentValueV2( - selectedItemIdComponentState, - dropdownId, - ); - - if (isDisabled === true) { + if (isDisabled) { return ( - - } - dropdownComponents={ - - - operand, - )} - selectableListInstanceId={dropdownId} - > - {operandsForFilterType.map((filterOperand, index) => ( - { - handleOperandChange(filterOperand); - }} - > - { - handleOperandChange(filterOperand); - }} - text={getOperandLabel(filterOperand)} - /> - - ))} - - - - } - dropdownHotkeyScope={{ scope: dropdownId }} - dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET} - dropdownPlacement="bottom-start" + + ); diff --git a/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx new file mode 100644 index 000000000..45c825ccc --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelectContent.tsx @@ -0,0 +1,98 @@ +import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset'; +import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand'; + +import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel'; +import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { RecordFilterOperand } from '@/object-record/record-filter/types/RecordFilterOperand'; +import { SelectControl } from '@/ui/input/components/SelectControl'; +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; +import { MenuItem } from 'twenty-ui/navigation'; + +type AdvancedFilterRecordFilterOperandSelectContentProps = { + recordFilterId: string; + filter: RecordFilter; + operandsForFilterType: readonly RecordFilterOperand[]; +}; + +export const AdvancedFilterRecordFilterOperandSelectContent = ({ + recordFilterId, + filter, + operandsForFilterType, +}: AdvancedFilterRecordFilterOperandSelectContentProps) => { + const dropdownId = `advanced-filter-view-filter-operand-${recordFilterId}`; + + const { closeDropdown } = useDropdown(dropdownId); + + const { applyObjectFilterDropdownOperand } = + useApplyObjectFilterDropdownOperand(); + + const handleOperandChange = (operand: ViewFilterOperand) => { + closeDropdown(); + + applyObjectFilterDropdownOperand(operand); + }; + + const selectedItemId = useRecoilComponentValueV2( + selectedItemIdComponentState, + dropdownId, + ); + + return ( + + } + dropdownComponents={ + + + operand, + )} + selectableListInstanceId={dropdownId} + > + {operandsForFilterType.map((filterOperand, index) => ( + { + handleOperandChange(filterOperand); + }} + > + { + handleOperandChange(filterOperand); + }} + text={getOperandLabel(filterOperand)} + /> + + ))} + + + + } + dropdownHotkeyScope={{ scope: dropdownId }} + dropdownOffset={DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET} + dropdownPlacement="bottom-start" + /> + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormCountryMultiSelectInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormCountryMultiSelectInput.tsx new file mode 100644 index 000000000..28d6422cd --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-field/form-types/components/FormCountryMultiSelectInput.tsx @@ -0,0 +1,58 @@ +import { useMemo } from 'react'; + +import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput'; +import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent'; +import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata'; +import { useCountries } from '@/ui/input/components/internal/hooks/useCountries'; +import { IconComponentProps } from 'twenty-ui/display'; +import { SelectOption } from 'twenty-ui/input'; + +export const FormCountryMultiSelectInput = ({ + onChange, + defaultValue, + label, + readonly = false, + VariablePicker, +}: { + onChange: (value: string | FieldMultiSelectValue) => void; + defaultValue?: string | FieldMultiSelectValue; + label?: string; + readonly?: boolean; + VariablePicker?: VariablePickerComponent; +}) => { + const countries = useCountries(); + + const options: SelectOption[] = useMemo( + () => + countries.map(({ countryName, Flag }) => ({ + label: countryName, + value: countryName, + Icon: (props: IconComponentProps) => + Flag({ width: props.size, height: props.size }), + })), + [countries], + ); + + const onCountryChange = (value: string | FieldMultiSelectValue) => { + if (readonly) { + return; + } + + if (value === null) { + onChange(''); + } else { + onChange(value); + } + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts index 88628f587..82a53e4d5 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/compute-record-gql-operation-filter/turnRecordFilterIntoGqlOperationFilter.ts @@ -367,7 +367,9 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({ subFieldName, ) ) { - const parsedCurrencyCodes = JSON.parse(recordFilter.value) as string[]; + const parsedCurrencyCodes = arrayOfStringsOrVariablesSchema.parse( + recordFilter.value, + ); if (parsedCurrencyCodes.length === 0) return undefined; @@ -543,14 +545,11 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({ }; } else { if (subFieldName === 'addressCountry') { - const parsedCountryCodes = JSON.parse( + const parsedCountryCodes = arrayOfStringsOrVariablesSchema.parse( recordFilter.value, - ) as string[]; + ); - if ( - recordFilter.value === '[]' || - parsedCountryCodes.length === 0 - ) { + if (parsedCountryCodes.length === 0) { return {}; } diff --git a/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts b/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts index c3d83330c..ab8585598 100644 --- a/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts +++ b/packages/twenty-front/src/modules/object-record/record-filter/utils/getRecordFilterOperands.ts @@ -150,7 +150,7 @@ export const COMPOSITE_FIELD_FILTER_OPERANDS_MAP = { export const getRecordFilterOperands = ({ filterType, subFieldName, -}: GetRecordFilterOperandsParams) => { +}: GetRecordFilterOperandsParams): readonly RecordFilterOperand[] => { switch (filterType) { case 'TEXT': case 'EMAILS': diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn.tsx index 17ca45ca6..c0aeacf31 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn.tsx @@ -1,5 +1,4 @@ import { AdvancedFilterFieldSelectDropdownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton'; -import { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect'; import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown'; import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId'; import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; @@ -8,6 +7,7 @@ import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; import { WorkflowAdvancedFilterDropdownColumn } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterDropdownColumn'; import { WorkflowAdvancedFilterValueFormInput } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterFormInput'; import { WorkflowAdvancedFilterLogicalOperatorCell } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterLogicalOperatorCell'; +import { WorkflowAdvancedFilterRecordFilterOperandSelect } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterOperandSelect'; import styled from '@emotion/styled'; const StyledContainer = styled.div` @@ -47,9 +47,8 @@ export const WorkflowAdvancedFilterRecordFilterColumn = ({ - { + const currentRecordFilters = useRecoilComponentValueV2( + currentRecordFiltersComponentState, + ); + + const filter = currentRecordFilters.find( + (recordFilter) => recordFilter.id === recordFilterId, + ); + + const isDisabled = !filter?.fieldMetadataId; + + const filterType = filter?.type; + + const operandsForFilterType = isDefined(filterType) + ? getRecordFilterOperands({ + filterType, + subFieldName: filter?.subFieldName, + }).filter((operand) => operand !== RecordFilterOperand.IsRelative) + : []; + + if (isDisabled) { + return ( + + ); + } + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput.tsx index 3542ee53f..7b4bbc65d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput.tsx @@ -1,9 +1,10 @@ import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState'; -import { FormCountryCodeSelectInput } from '@/object-record/record-field/form-types/components/FormCountryCodeSelectInput'; -import { FormCountrySelectInput } from '@/object-record/record-field/form-types/components/FormCountrySelectInput'; +import { FormCountryMultiSelectInput } from '@/object-record/record-field/form-types/components/FormCountryMultiSelectInput'; +import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput'; import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput'; import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput'; import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; +import { CURRENCIES } from '@/settings/data-model/constants/Currencies'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { JsonValue } from 'type-fest'; @@ -25,8 +26,8 @@ export const WorkflowAdvancedFilterValueFormCompositeFieldInput = ({ <> {filterType === 'ADDRESS' ? ( subFieldNameUsedInDropdown === 'addressCountry' ? ( - @@ -39,10 +40,11 @@ export const WorkflowAdvancedFilterValueFormCompositeFieldInput = ({ ) ) : filterType === 'CURRENCY' ? ( recordFilter.subFieldName === 'currencyCode' ? ( - ) : recordFilter.subFieldName === 'amountMicros' ? (