Search action - Add variables to select and relations + other fixes (#12604)

- Variables can now be handled for select/multiselect/relations
- Hide field not supported in forms (source, rating)
- Add tests for schemas

Remaning issues:
- country/currency pickers not working
- stories for components
- variable picker hidden for dates
This commit is contained in:
Thomas Trompette
2025-06-16 13:45:28 +02:00
committed by GitHub
parent e0cb53af48
commit ae57e67c77
33 changed files with 621 additions and 234 deletions

View File

@ -1,10 +0,0 @@
import styled from '@emotion/styled';
const StyledColumn = styled.div`
display: flex;
flex-direction: column;
width: 100%;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const AdvancedFilterDropdownColumn = StyledColumn;

View File

@ -1,18 +1,15 @@
import { AdvancedFilterLogicalOperatorDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown';
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import styled from '@emotion/styled';
import { useContext } from 'react';
import { capitalize } from 'twenty-shared/utils';
const StyledText = styled.div<{ noPadding?: boolean }>`
const StyledText = styled.div`
height: ${({ theme }) => theme.spacing(8)};
display: flex;
align-items: center;
padding-left: ${({ theme, noPadding }) =>
noPadding ? 0 : theme.spacing(2.25)};
padding-left: ${({ theme }) => theme.spacing(2.25)};
`;
const StyledContainer = styled.div`
@ -31,18 +28,16 @@ export const AdvancedFilterLogicalOperatorCell = ({
index,
recordFilterGroup,
}: AdvancedFilterLogicalOperatorCellProps) => {
const { isColumn } = useContext(AdvancedFilterContext);
return (
<StyledContainer>
{index === 0 ? (
<StyledText noPadding={isColumn}>Where</StyledText>
<StyledText>Where</StyledText>
) : index === 1 ? (
<AdvancedFilterLogicalOperatorDropdown
recordFilterGroup={recordFilterGroup}
/>
) : (
<StyledText noPadding={isColumn}>
<StyledText>
{capitalize(recordFilterGroup.logicalOperator.toLowerCase())}
</StyledText>
)}

View File

@ -1,36 +0,0 @@
import { AdvancedFilterRecordFilterColumn } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterColumn';
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useContext } from 'react';
export const AdvancedFilterRecordFilter = ({
recordFilterGroup,
recordFilter,
recordFilterIndex,
VariablePicker,
}: {
recordFilterGroup: RecordFilterGroup;
recordFilter: RecordFilter;
recordFilterIndex: number;
VariablePicker?: VariablePickerComponent;
}) => {
const { isColumn } = useContext(AdvancedFilterContext);
return isColumn ? (
<AdvancedFilterRecordFilterColumn
recordFilterGroup={recordFilterGroup}
recordFilter={recordFilter}
recordFilterIndex={recordFilterIndex}
VariablePicker={VariablePicker}
/>
) : (
<AdvancedFilterRecordFilterRow
recordFilterGroup={recordFilterGroup}
recordFilter={recordFilter}
recordFilterIndex={recordFilterIndex}
/>
);
};

View File

@ -1,63 +0,0 @@
import { AdvancedFilterDropdownColumn } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownColumn';
import { AdvancedFilterFieldSelectDropdownButton } from '@/object-record/advanced-filter/components/AdvancedFilterFieldSelectDropdownButton';
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
import { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect';
import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown';
import { AdvancedFilterValueFormInput } from '@/object-record/advanced-filter/components/AdvancedFilterValueFormInput';
import { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import styled from '@emotion/styled';
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const AdvancedFilterRecordFilterColumn = ({
recordFilterGroup,
recordFilter,
recordFilterIndex,
VariablePicker,
}: {
recordFilterGroup: RecordFilterGroup;
recordFilter: RecordFilter;
recordFilterIndex: number;
VariablePicker?: VariablePickerComponent;
}) => {
return (
<ObjectFilterDropdownComponentInstanceContext.Provider
value={{
instanceId: getAdvancedFilterObjectFilterDropdownComponentInstanceId(
recordFilter.id,
),
}}
>
<AdvancedFilterDropdownColumn>
<StyledContainer>
<AdvancedFilterLogicalOperatorCell
index={recordFilterIndex}
recordFilterGroup={recordFilterGroup}
/>
<AdvancedFilterRecordFilterOptionsDropdown
recordFilterId={recordFilter.id}
/>
</StyledContainer>
<AdvancedFilterFieldSelectDropdownButton
recordFilterId={recordFilter.id}
/>
<AdvancedFilterRecordFilterOperandSelect
recordFilterId={recordFilter.id}
/>
<AdvancedFilterValueFormInput
recordFilterId={recordFilter.id}
VariablePicker={VariablePicker}
/>
</AdvancedFilterDropdownColumn>
</ObjectFilterDropdownComponentInstanceContext.Provider>
);
};

View File

@ -1,36 +0,0 @@
import { AdvancedFilterRecordFilterGroupColumn } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupColumn';
import { AdvancedFilterRecordFilterGroupRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupRow';
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import { useContext } from 'react';
export const AdvancedFilterRecordFilterGroup = ({
parentRecordFilterGroup,
recordFilterGroup,
recordFilterGroupIndex,
VariablePicker,
}: {
parentRecordFilterGroup: RecordFilterGroup;
recordFilterGroup: RecordFilterGroup;
recordFilterGroupIndex: number;
VariablePicker?: VariablePickerComponent;
}) => {
const { isColumn } = useContext(AdvancedFilterContext);
return isColumn ? (
<AdvancedFilterRecordFilterGroupColumn
parentRecordFilterGroup={parentRecordFilterGroup}
recordFilterGroup={recordFilterGroup}
recordFilterGroupIndex={recordFilterGroupIndex}
VariablePicker={VariablePicker}
/>
) : (
<AdvancedFilterRecordFilterGroupRow
parentRecordFilterGroup={parentRecordFilterGroup}
recordFilterGroup={recordFilterGroup}
recordFilterGroupIndex={recordFilterGroupIndex}
VariablePicker={VariablePicker}
/>
);
};

View File

@ -1,8 +1,7 @@
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
import { AdvancedFilterRecordFilter } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilter';
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import styled from '@emotion/styled';
import { isDefined } from 'twenty-shared/utils';
@ -17,17 +16,14 @@ const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
flex-direction: column;
gap: ${({ theme }) => theme.spacing(2)};
padding: ${({ theme }) => theme.spacing(2)};
overflow: hidden;
`;
type AdvancedFilterRecordFilterGroupChildrenProps = {
recordFilterGroupId: string;
VariablePicker?: VariablePickerComponent;
};
export const AdvancedFilterRecordFilterGroupChildren = ({
recordFilterGroupId,
VariablePicker,
}: AdvancedFilterRecordFilterGroupChildrenProps) => {
const { currentRecordFilterGroup, childRecordFilters } =
useChildRecordFiltersAndRecordFilterGroups({
@ -45,12 +41,11 @@ export const AdvancedFilterRecordFilterGroupChildren = ({
return (
<StyledContainer isGrayBackground={hasParentRecordFilterGroup}>
{childRecordFilters.map((childRecordFilter, childRecordFilterIndex) => (
<AdvancedFilterRecordFilter
<AdvancedFilterRecordFilterRow
key={childRecordFilter.id}
recordFilter={childRecordFilter}
recordFilterIndex={childRecordFilterIndex}
recordFilterGroup={currentRecordFilterGroup}
VariablePicker={VariablePicker}
/>
))}
<AdvancedFilterAddFilterRuleSelect

View File

@ -1,44 +0,0 @@
import { AdvancedFilterDropdownColumn } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownColumn';
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
import { AdvancedFilterRecordFilterGroupChildren } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupChildren';
import { AdvancedFilterRecordFilterGroupOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
import styled from '@emotion/styled';
const StyledContainer = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
gap: ${({ theme }) => theme.spacing(1)};
`;
export const AdvancedFilterRecordFilterGroupColumn = ({
parentRecordFilterGroup,
recordFilterGroup,
recordFilterGroupIndex,
VariablePicker,
}: {
parentRecordFilterGroup: RecordFilterGroup;
recordFilterGroup: RecordFilterGroup;
recordFilterGroupIndex: number;
VariablePicker?: VariablePickerComponent;
}) => {
return (
<AdvancedFilterDropdownColumn>
<StyledContainer>
<AdvancedFilterLogicalOperatorCell
index={recordFilterGroupIndex}
recordFilterGroup={parentRecordFilterGroup}
/>
<AdvancedFilterRecordFilterGroupOptionsDropdown
recordFilterGroupId={recordFilterGroup.id}
/>
</StyledContainer>
<AdvancedFilterRecordFilterGroupChildren
recordFilterGroupId={recordFilterGroup.id}
VariablePicker={VariablePicker}
/>
</AdvancedFilterDropdownColumn>
);
};

View File

@ -2,19 +2,16 @@ import { AdvancedFilterDropdownRow } from '@/object-record/advanced-filter/compo
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
import { AdvancedFilterRecordFilterGroupChildren } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupChildren';
import { AdvancedFilterRecordFilterGroupOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
export const AdvancedFilterRecordFilterGroupRow = ({
parentRecordFilterGroup,
recordFilterGroup,
recordFilterGroupIndex,
VariablePicker,
}: {
parentRecordFilterGroup: RecordFilterGroup;
recordFilterGroup: RecordFilterGroup;
recordFilterGroupIndex: number;
VariablePicker?: VariablePickerComponent;
}) => {
return (
<AdvancedFilterDropdownRow>
@ -24,7 +21,6 @@ export const AdvancedFilterRecordFilterGroupRow = ({
/>
<AdvancedFilterRecordFilterGroupChildren
recordFilterGroupId={recordFilterGroup.id}
VariablePicker={VariablePicker}
/>
<AdvancedFilterRecordFilterGroupOptionsDropdown
recordFilterGroupId={recordFilterGroup.id}

View File

@ -1,5 +1,4 @@
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
@ -17,20 +16,21 @@ import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import styled from '@emotion/styled';
import { useContext } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { MenuItem } from 'twenty-ui/navigation';
const StyledContainer = styled.div<{ isColumn?: boolean }>`
width: ${({ isColumn }) => (isColumn ? 'auto' : '100px')};
const StyledContainer = styled.div<{ width?: string }>`
width: ${({ width }) => width ?? '100px'};
`;
type AdvancedFilterRecordFilterOperandSelectProps = {
recordFilterId: string;
widthFromProps?: string;
};
export const AdvancedFilterRecordFilterOperandSelect = ({
recordFilterId,
widthFromProps,
}: AdvancedFilterRecordFilterOperandSelectProps) => {
const dropdownId = `advanced-filter-view-filter-operand-${recordFilterId}`;
@ -38,8 +38,6 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
currentRecordFiltersComponentState,
);
const { isColumn } = useContext(AdvancedFilterContext);
const filter = currentRecordFilters.find(
(recordFilter) => recordFilter.id === recordFilterId,
);
@ -86,7 +84,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
}
return (
<StyledContainer isColumn={isColumn}>
<StyledContainer width={widthFromProps}>
<Dropdown
dropdownId={dropdownId}
clickableComponent={

View File

@ -1,6 +1,6 @@
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
import { AdvancedFilterRecordFilter } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilter';
import { AdvancedFilterRecordFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroup';
import { AdvancedFilterRecordFilterGroupRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupRow';
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
import { ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth';
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
@ -45,14 +45,14 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
isRecordFilterGroupChildARecordFilterGroup(
recordFilterGroupChild,
) ? (
<AdvancedFilterRecordFilterGroup
<AdvancedFilterRecordFilterGroupRow
key={recordFilterGroupChild.id}
parentRecordFilterGroup={rootRecordFilterGroup}
recordFilterGroup={recordFilterGroupChild}
recordFilterGroupIndex={recordFilterGroupChildIndex}
/>
) : (
<AdvancedFilterRecordFilter
<AdvancedFilterRecordFilterRow
key={recordFilterGroupChild.id}
recordFilterGroup={rootRecordFilterGroup}
recordFilter={recordFilterGroupChild}

View File

@ -1,79 +0,0 @@
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 { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { JsonValue } from 'type-fest';
export const AdvancedFilterValueFormCompositeFieldInput = ({
recordFilter,
onChange,
VariablePicker,
}: {
recordFilter: RecordFilter;
onChange: (newValue: JsonValue) => void;
VariablePicker?: VariablePickerComponent;
}) => {
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
subFieldNameUsedInDropdownComponentState,
);
const filterType = recordFilter.type;
return (
<>
{filterType === 'ADDRESS' ? (
subFieldNameUsedInDropdown === 'addressCountry' ? (
<FormCountrySelectInput
selectedCountryName={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
) : (
<FormTextFieldInput
defaultValue={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
)
) : filterType === 'CURRENCY' ? (
recordFilter.subFieldName === 'currencyCode' ? (
<FormCountryCodeSelectInput
selectedCountryCode={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
) : recordFilter.subFieldName === 'amountMicros' ? (
<FormNumberFieldInput
defaultValue={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
) : null
) : filterType === 'PHONES' ? (
recordFilter.subFieldName === 'primaryPhoneNumber' ? (
<FormNumberFieldInput
defaultValue={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
) : (
<FormTextFieldInput
defaultValue={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
)
) : (
<FormTextFieldInput
defaultValue={recordFilter.value}
onChange={onChange}
VariablePicker={VariablePicker}
/>
)}
</>
);
};

View File

@ -1,92 +0,0 @@
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { AdvancedFilterValueFormCompositeFieldInput } from '@/object-record/advanced-filter/components/AdvancedFilterValueFormCompositeFieldInput';
import { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isObject } from '@sniptt/guards';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { JsonValue } from 'type-fest';
export const AdvancedFilterValueFormInput = ({
recordFilterId,
VariablePicker,
}: {
recordFilterId: string;
VariablePicker?: VariablePickerComponent;
}) => {
const currentRecordFilters = useRecoilComponentValueV2(
currentRecordFiltersComponentState,
);
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
subFieldNameUsedInDropdownComponentState,
);
const recordFilter = currentRecordFilters.find(
(recordFilter) => recordFilter.id === recordFilterId,
);
const isDisabled = !recordFilter?.fieldMetadataId || !recordFilter.operand;
const { applyObjectFilterDropdownFilterValue } =
useApplyObjectFilterDropdownFilterValue();
const handleChange = (newValue: JsonValue) => {
if (typeof newValue === 'string') {
applyObjectFilterDropdownFilterValue(newValue);
} else if (Array.isArray(newValue) || isObject(newValue)) {
applyObjectFilterDropdownFilterValue(JSON.stringify(newValue));
} else {
applyObjectFilterDropdownFilterValue(String(newValue));
}
};
const fieldMetadataItemUsedInDropdown = useRecoilComponentValueV2(
fieldMetadataItemUsedInDropdownComponentSelector,
);
const fieldDefinition = fieldMetadataItemUsedInDropdown
? formatFieldMetadataItemAsFieldDefinition({
field: fieldMetadataItemUsedInDropdown,
objectMetadataItem: objectMetadataItem,
})
: null;
if (isDisabled) {
return null;
}
if (isDefined(subFieldNameUsedInDropdown)) {
return (
<AdvancedFilterValueFormCompositeFieldInput
recordFilter={recordFilter}
VariablePicker={VariablePicker}
onChange={handleChange}
/>
);
}
const field = {
type: recordFilter.type as FieldMetadataType,
label: '',
metadata: fieldDefinition?.metadata as FieldMetadata,
};
return (
<FormFieldInput
field={field}
defaultValue={recordFilter.value}
onChange={handleChange}
VariablePicker={VariablePicker}
/>
);
};

View File

@ -2,14 +2,12 @@ import { AdvancedFilterDropdownFilterInput } from '@/object-record/advanced-filt
import { AdvancedFilterDropdownTextInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput';
import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect';
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
import { shouldShowFilterTextInput } from '@/object-record/advanced-filter/utils/shouldShowFilterTextInput';
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
@ -17,7 +15,6 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import styled from '@emotion/styled';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
const StyledValueDropdownContainer = styled.div`
@ -83,31 +80,10 @@ export const AdvancedFilterValueInput = ({
? ({ y: -33, x: 0 } satisfies DropdownOffset)
: DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET;
const isFilterableByTextValue =
isDefined(filterType) &&
(TEXT_FILTER_TYPES.includes(filterType) ||
NUMBER_FILTER_TYPES.includes(filterType));
const isCurrencyAmountMicrosFilter = isExpectedSubFieldName(
FieldMetadataType.CURRENCY,
'amountMicros',
recordFilter.subFieldName,
);
const isAddressFilterOnSubFieldOtherThanCountry =
filterType === 'ADDRESS' && subFieldNameUsedInDropdown !== 'addressCountry';
const isActorNameFilter = isExpectedSubFieldName(
FieldMetadataType.ACTOR,
'name',
recordFilter.subFieldName,
);
const showFilterTextInputInsteadOfDropdown =
isFilterableByTextValue ||
isCurrencyAmountMicrosFilter ||
isAddressFilterOnSubFieldOtherThanCountry ||
isActorNameFilter;
const showFilterTextInputInsteadOfDropdown = shouldShowFilterTextInput({
recordFilter,
subFieldNameUsedInDropdown,
});
return (
<StyledValueDropdownContainer>

View File

@ -2,7 +2,7 @@ import { createContext } from 'react';
type AdvancedFilterContextType = {
onUpdate?: () => void;
isColumn?: boolean;
isWorkflowFindRecords?: boolean;
};
export const AdvancedFilterContext = createContext<AdvancedFilterContextType>(

View File

@ -0,0 +1,42 @@
import { NUMBER_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/NumberFilterTypes';
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
import { isExpectedSubFieldName } from '@/object-record/object-filter-dropdown/utils/isExpectedSubFieldName';
import { FieldMetadataType } from 'twenty-shared/types';
import { RecordFilter } from '../../record-filter/types/RecordFilter';
import { CompositeFieldSubFieldName } from '@/settings/data-model/types/CompositeFieldSubFieldName';
export const shouldShowFilterTextInput = ({
recordFilter,
subFieldNameUsedInDropdown,
}: {
recordFilter: RecordFilter;
subFieldNameUsedInDropdown: CompositeFieldSubFieldName | null | undefined;
}) => {
const isFilterableByTextValue =
TEXT_FILTER_TYPES.includes(recordFilter.type) ||
NUMBER_FILTER_TYPES.includes(recordFilter.type);
const isCurrencyAmountMicrosFilter = isExpectedSubFieldName(
FieldMetadataType.CURRENCY,
'amountMicros',
recordFilter.subFieldName,
);
const isAddressFilterOnSubFieldOtherThanCountry =
recordFilter.type === 'ADDRESS' &&
subFieldNameUsedInDropdown !== 'addressCountry';
const isActorNameFilter = isExpectedSubFieldName(
FieldMetadataType.ACTOR,
'name',
recordFilter.subFieldName,
);
return (
isFilterableByTextValue ||
isCurrencyAmountMicrosFilter ||
isAddressFilterOnSubFieldOtherThanCountry ||
isActorNameFilter
);
};

View File

@ -15,8 +15,8 @@ import { SelectableItem } from '@/object-record/select/types/SelectableItem';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue';
import { arrayOfUuidOrVariableSchema } from '@/views/view-filter-value/validation-schemas/arrayOfUuidsOrVariablesSchema';
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
import { isDefined } from 'twenty-shared/utils';
import { IconUserCircle } from 'twenty-ui/display';
@ -59,7 +59,7 @@ export const ObjectFilterDropdownRecordSelect = ({
const { isCurrentWorkspaceMemberSelected } = jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: simpleRelationFilterValueSchema.parse(
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
objectFilterDropdownFilterValue,
),
})
@ -105,7 +105,7 @@ export const ObjectFilterDropdownRecordSelect = ({
const { selectedRecordIds } = jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: simpleRelationFilterValueSchema.parse(
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
recordFilterUsedInDropdown?.value,
),
})

View File

@ -12,11 +12,13 @@ export type FormCountryCodeSelectInputUpdatedValue = CountryCode | '';
export const FormCountryCodeSelectInput = ({
selectedCountryCode,
onChange,
label,
readonly = false,
VariablePicker,
}: {
selectedCountryCode: string;
onChange: (countryCode: FormCountryCodeSelectInputUpdatedValue) => void;
label?: string;
readonly?: boolean;
VariablePicker?: VariablePickerComponent;
}) => {
@ -55,7 +57,7 @@ export const FormCountryCodeSelectInput = ({
return (
<FormSelectFieldInput
label="Country Code"
label={label}
onChange={onCountryCodeChange}
options={options}
defaultValue={selectedCountryCode}

View File

@ -55,13 +55,14 @@ export const FormPhoneFieldInput = ({
{label && <InputLabel>{label}</InputLabel>}
<FormNestedFieldInputContainer>
<FormCountryCodeSelectInput
selectedCountryCode={defaultValue?.primaryPhoneCountryCode || ''}
label="Country Code"
selectedCountryCode={defaultValue?.primaryPhoneCountryCode ?? ''}
onChange={handleCountryChange}
readonly={readonly}
/>
<FormNumberFieldInput
label="Phone Number"
defaultValue={defaultValue?.primaryPhoneNumber || ''}
defaultValue={defaultValue?.primaryPhoneNumber ?? ''}
onChange={handleNumberChange}
VariablePicker={VariablePicker}
placeholder="Enter phone number"

View File

@ -6,7 +6,9 @@ import { FormCountryCodeSelectInput } from '../FormCountryCodeSelectInput';
const meta: Meta<typeof FormCountryCodeSelectInput> = {
title: 'UI/Data/Field/Form/Input/FormCountryCodeSelectInput',
component: FormCountryCodeSelectInput,
args: {},
args: {
label: 'Country Code',
},
argTypes: {},
};

View File

@ -1,12 +1,25 @@
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
import { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
import { shouldDisplayFormField } from '@/workflow/workflow-steps/workflow-actions/utils/shouldDisplayFormField';
import { useContext } from 'react';
export const useFilterableFieldMetadataItemsInRecordIndexContext = () => {
const { objectMetadataItem } = useRecordIndexContextOrThrow();
const { isWorkflowFindRecords } = useContext(AdvancedFilterContext);
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
objectMetadataItem.id,
);
const {
filterableFieldMetadataItems: filterableFieldMetadataItemsForRecordIndex,
} = useFilterableFieldMetadataItems(objectMetadataItem.id);
const filterableFieldMetadataItems = isWorkflowFindRecords
? filterableFieldMetadataItemsForRecordIndex.filter((fieldMetadataItem) =>
shouldDisplayFormField({
fieldMetadataItem,
actionType: 'FIND_RECORDS',
}),
)
: filterableFieldMetadataItemsForRecordIndex;
return { filterableFieldMetadataItems };
};

View File

@ -33,9 +33,7 @@ import { RecordFilterValueDependencies } from '@/object-record/record-filter/typ
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
import { resolveSelectViewFilterValue } from '@/views/view-filter-value/utils/resolveSelectViewFilterValue';
import { jsonRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/jsonRelationFilterValueSchema';
import { simpleRelationFilterValueSchema } from '@/views/view-filter-value/validation-schemas/simpleRelationFilterValueSchema';
import { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
import { z } from 'zod';
@ -44,6 +42,8 @@ import { checkIfShouldComputeEmptinessFilter } from '@/object-record/record-filt
import { checkIfShouldSkipFiltering } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/checkIfShouldSkipFiltering';
import { computeGqlOperationFilterForEmails } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/for-composite-field/computeGqlOperationFilterForEmails';
import { computeGqlOperationFilterForLinks } from '@/object-record/record-filter/utils/compute-record-gql-operation-filter/for-composite-field/computeGqlOperationFilterForLinks';
import { arrayOfStringsOrVariablesSchema } from '@/views/view-filter-value/validation-schemas/arrayOfStringsOrVariablesSchema';
import { arrayOfUuidOrVariableSchema } from '@/views/view-filter-value/validation-schemas/arrayOfUuidsOrVariablesSchema';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
@ -312,7 +312,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
jsonRelationFilterValueSchema
.catch({
isCurrentWorkspaceMemberSelected: false,
selectedRecordIds: simpleRelationFilterValueSchema.parse(
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
recordFilter.value,
),
})
@ -759,7 +759,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
);
}
case 'MULTI_SELECT': {
const options = resolveSelectViewFilterValue(recordFilter);
const options = arrayOfStringsOrVariablesSchema.parse(recordFilter.value);
if (options.length === 0) return;
@ -817,7 +817,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
}
}
case 'SELECT': {
const options = resolveSelectViewFilterValue(recordFilter);
const options = arrayOfStringsOrVariablesSchema.parse(recordFilter.value);
if (options.length === 0) return;