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:
@ -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>
|
||||
)}
|
||||
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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={
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -2,7 +2,7 @@ import { createContext } from 'react';
|
||||
|
||||
type AdvancedFilterContextType = {
|
||||
onUpdate?: () => void;
|
||||
isColumn?: boolean;
|
||||
isWorkflowFindRecords?: boolean;
|
||||
};
|
||||
|
||||
export const AdvancedFilterContext = createContext<AdvancedFilterContextType>(
|
||||
|
||||
@ -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
|
||||
);
|
||||
};
|
||||
@ -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,
|
||||
),
|
||||
})
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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: {},
|
||||
};
|
||||
|
||||
|
||||
@ -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 };
|
||||
};
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
import { ViewFilter } from '@/views/types/ViewFilter';
|
||||
import { z } from 'zod';
|
||||
|
||||
const selectViewFilterValueSchema = z
|
||||
.string()
|
||||
.transform((val) => (val === '' ? [] : JSON.parse(val)))
|
||||
.refine(
|
||||
(parsed) =>
|
||||
Array.isArray(parsed) && parsed.every((item) => typeof item === 'string'),
|
||||
{
|
||||
message: 'Expected an array of strings',
|
||||
},
|
||||
);
|
||||
|
||||
export const resolveSelectViewFilterValue = (
|
||||
viewFilter: Pick<ViewFilter, 'value'>,
|
||||
) => {
|
||||
return selectViewFilterValueSchema.parse(viewFilter.value);
|
||||
};
|
||||
@ -0,0 +1,91 @@
|
||||
import { arrayOfStringsOrVariablesSchema } from '../arrayOfStringsOrVariablesSchema';
|
||||
|
||||
describe('arrayOfStringsOrVariablesSchema', () => {
|
||||
describe('Empty value handling', () => {
|
||||
it('should return empty array for empty string', () => {
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse('');
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variable syntax validation', () => {
|
||||
it('should accept valid variable syntax', () => {
|
||||
const validVariables = [
|
||||
'{{variable}}',
|
||||
'{{user.id}}',
|
||||
'{{company.name}}',
|
||||
];
|
||||
|
||||
validVariables.forEach((variable) => {
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse(variable);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([variable]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('JSON array handling', () => {
|
||||
it('should accept valid JSON array of strings', () => {
|
||||
const validArrays = [
|
||||
JSON.stringify(['value1', 'value2']),
|
||||
JSON.stringify(['{{variable1}}', '{{variable2}}']),
|
||||
JSON.stringify(['value1', '{{variable2}}']),
|
||||
];
|
||||
|
||||
validArrays.forEach((array) => {
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse(array);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(JSON.parse(array));
|
||||
}
|
||||
});
|
||||
});
|
||||
it('should reject JSON array with non-string values', () => {
|
||||
const invalidArrays = [
|
||||
JSON.stringify([1, 2, 3]),
|
||||
JSON.stringify([true, false]),
|
||||
JSON.stringify([null]),
|
||||
JSON.stringify([{}]),
|
||||
JSON.stringify([[]]),
|
||||
];
|
||||
|
||||
invalidArrays.forEach((array) => {
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse(array);
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Edge cases', () => {
|
||||
it('should handle whitespace in variable syntax', () => {
|
||||
const result =
|
||||
arrayOfStringsOrVariablesSchema.safeParse('{{ variable }}');
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(['{{ variable }}']);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle nested variables in JSON array', () => {
|
||||
const input = JSON.stringify(['{{outer.{{inner}}}}']);
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(['{{outer.{{inner}}}}']);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle empty array in JSON', () => {
|
||||
const result = arrayOfStringsOrVariablesSchema.safeParse('[]');
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,169 @@
|
||||
import { arrayOfUuidOrVariableSchema } from '../arrayOfUuidsOrVariablesSchema';
|
||||
|
||||
describe('arrayOfUuidOrVariableSchema', () => {
|
||||
describe('UUID validation', () => {
|
||||
it('should accept valid UUIDs', () => {
|
||||
const validUuids = [
|
||||
'123e4567-e89b-12d3-a456-426614174000',
|
||||
'550e8400-e29b-41d4-a716-446655440000',
|
||||
];
|
||||
|
||||
validUuids.forEach((uuid) => {
|
||||
// Test as single value
|
||||
const singleResult = arrayOfUuidOrVariableSchema.safeParse(uuid);
|
||||
expect(singleResult.success).toBe(true);
|
||||
if (singleResult.success) {
|
||||
expect(singleResult.data).toEqual([uuid]);
|
||||
}
|
||||
|
||||
// Test as array
|
||||
const arrayResult = arrayOfUuidOrVariableSchema.safeParse([uuid]);
|
||||
expect(arrayResult.success).toBe(true);
|
||||
if (arrayResult.success) {
|
||||
expect(arrayResult.data).toEqual([uuid]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array for invalid UUIDs', () => {
|
||||
const invalidUuids = [
|
||||
'invalid-uuid',
|
||||
'12345',
|
||||
'550e8400e29b41d4a716446655440000',
|
||||
'',
|
||||
'123e4567-e89b-12d3-a456-42661417400-',
|
||||
];
|
||||
|
||||
invalidUuids.forEach((uuid) => {
|
||||
// Test as single value
|
||||
const singleResult = arrayOfUuidOrVariableSchema.safeParse(uuid);
|
||||
expect(singleResult.success).toBe(true);
|
||||
if (singleResult.success) {
|
||||
expect(singleResult.data).toEqual([]);
|
||||
}
|
||||
|
||||
// Test as array
|
||||
const arrayResult = arrayOfUuidOrVariableSchema.safeParse([uuid]);
|
||||
expect(arrayResult.success).toBe(true);
|
||||
if (arrayResult.success) {
|
||||
expect(arrayResult.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Variable syntax validation', () => {
|
||||
it('should accept valid variable syntax', () => {
|
||||
const validVariables = [
|
||||
'{{variable}}',
|
||||
'{{user.id}}',
|
||||
'{{company.name}}',
|
||||
];
|
||||
|
||||
validVariables.forEach((variable) => {
|
||||
// Test as single value
|
||||
const singleResult = arrayOfUuidOrVariableSchema.safeParse(variable);
|
||||
expect(singleResult.success).toBe(true);
|
||||
if (singleResult.success) {
|
||||
expect(singleResult.data).toEqual([variable]);
|
||||
}
|
||||
|
||||
// Test as array
|
||||
const arrayResult = arrayOfUuidOrVariableSchema.safeParse([variable]);
|
||||
expect(arrayResult.success).toBe(true);
|
||||
if (arrayResult.success) {
|
||||
expect(arrayResult.data).toEqual([variable]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array for invalid variable syntax', () => {
|
||||
const invalidVariables = ['{{variable', 'variable}}', '{{}}', '{{', '}}'];
|
||||
|
||||
invalidVariables.forEach((variable) => {
|
||||
// Test as single value
|
||||
const singleResult = arrayOfUuidOrVariableSchema.safeParse(variable);
|
||||
expect(singleResult.success).toBe(true);
|
||||
if (singleResult.success) {
|
||||
expect(singleResult.data).toEqual([]);
|
||||
}
|
||||
|
||||
// Test as array
|
||||
const arrayResult = arrayOfUuidOrVariableSchema.safeParse([variable]);
|
||||
expect(arrayResult.success).toBe(true);
|
||||
if (arrayResult.success) {
|
||||
expect(arrayResult.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input type handling', () => {
|
||||
it('should handle string input with valid JSON', () => {
|
||||
const input = JSON.stringify(['123e4567-e89b-12d3-a456-426614174000']);
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(['123e4567-e89b-12d3-a456-426614174000']);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle string input with variables', () => {
|
||||
const input = '{{variable}}';
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(['{{variable}}']);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle array input directly', () => {
|
||||
const input = ['123e4567-e89b-12d3-a456-426614174000', '{{variable}}'];
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual(input);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle single value input', () => {
|
||||
const input = '20202020-0687-4c41-b707-ed1bfca972a7';
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([input]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error handling', () => {
|
||||
it('should return empty array for invalid JSON string', () => {
|
||||
const input = 'invalid-json';
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return empty array for non-string, non-array input', () => {
|
||||
const inputs = [null, undefined, 123, true, {}];
|
||||
inputs.forEach((input) => {
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty array for array with invalid values', () => {
|
||||
const input = ['invalid-uuid', 'not-a-variable'];
|
||||
const result = arrayOfUuidOrVariableSchema.safeParse(input);
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toEqual([]);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,19 @@
|
||||
import { isValidVariable } from 'twenty-shared/utils';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const arrayOfStringsOrVariablesSchema = z
|
||||
.string()
|
||||
.transform((val) => {
|
||||
if (val === '') return [];
|
||||
if (isValidVariable(val) as boolean) {
|
||||
return [val];
|
||||
}
|
||||
return JSON.parse(val);
|
||||
})
|
||||
.refine(
|
||||
(parsed) =>
|
||||
Array.isArray(parsed) && parsed.every((item) => typeof item === 'string'),
|
||||
{
|
||||
message: 'Expected an array of strings',
|
||||
},
|
||||
);
|
||||
@ -0,0 +1,30 @@
|
||||
import { isValidUuid, isValidVariable } from 'twenty-shared/utils';
|
||||
import { z } from 'zod';
|
||||
|
||||
export const arrayOfUuidOrVariableSchema = z
|
||||
.preprocess(
|
||||
(value) => {
|
||||
try {
|
||||
if (typeof value === 'string') {
|
||||
if (isValidVariable(value) as boolean) {
|
||||
return [value];
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) ? parsed : [parsed];
|
||||
} catch {
|
||||
return [value];
|
||||
}
|
||||
}
|
||||
return Array.isArray(value) ? value : [value];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
z.array(
|
||||
z.string().refine((val) => {
|
||||
return isValidUuid(val) || isValidVariable(val);
|
||||
}, 'Must be a valid UUID or a variable with {{ }} syntax'),
|
||||
),
|
||||
)
|
||||
.catch([]);
|
||||
@ -1,11 +0,0 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const simpleRelationFilterValueSchema = z
|
||||
.preprocess((value) => {
|
||||
try {
|
||||
return typeof value === 'string' ? JSON.parse(value) : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}, z.array(z.string().uuid()))
|
||||
.catch([]);
|
||||
@ -7,4 +7,4 @@ const StyledColumn = styled.div`
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const AdvancedFilterDropdownColumn = StyledColumn;
|
||||
export const WorkflowAdvancedFilterDropdownColumn = StyledColumn;
|
||||
@ -1,25 +1,31 @@
|
||||
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
|
||||
import { AdvancedFilterValueFormCompositeFieldInput } from '@/object-record/advanced-filter/components/AdvancedFilterValueFormCompositeFieldInput';
|
||||
import { shouldShowFilterTextInput } from '@/object-record/advanced-filter/utils/shouldShowFilterTextInput';
|
||||
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 { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
||||
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 { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
|
||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
||||
import {
|
||||
FieldMetadata,
|
||||
FieldMultiSelectMetadata,
|
||||
FieldSelectMetadata,
|
||||
} 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 { WorkflowAdvancedFilterValueFormCompositeFieldInput } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterValueFormCompositeFieldInput';
|
||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
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 = ({
|
||||
export const WorkflowAdvancedFilterValueFormInput = ({
|
||||
recordFilterId,
|
||||
VariablePicker,
|
||||
}: {
|
||||
recordFilterId: string;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
}) => {
|
||||
const currentRecordFilters = useRecoilComponentValueV2(
|
||||
currentRecordFiltersComponentState,
|
||||
@ -37,6 +43,11 @@ export const AdvancedFilterValueFormInput = ({
|
||||
|
||||
const isDisabled = !recordFilter?.fieldMetadataId || !recordFilter.operand;
|
||||
|
||||
const operandHasNoInput =
|
||||
(recordFilter &&
|
||||
!configurableViewFilterOperands.has(recordFilter.operand)) ??
|
||||
true;
|
||||
|
||||
const { applyObjectFilterDropdownFilterValue } =
|
||||
useApplyObjectFilterDropdownFilterValue();
|
||||
|
||||
@ -61,20 +72,63 @@ export const AdvancedFilterValueFormInput = ({
|
||||
})
|
||||
: null;
|
||||
|
||||
if (isDisabled) {
|
||||
if (!isDefined(recordFilter)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isFilterableByTextValue = shouldShowFilterTextInput({
|
||||
recordFilter,
|
||||
subFieldNameUsedInDropdown,
|
||||
});
|
||||
|
||||
const isFilterableByMultiSelectValue =
|
||||
recordFilter.type === FieldMetadataType.MULTI_SELECT ||
|
||||
recordFilter.type === FieldMetadataType.SELECT;
|
||||
|
||||
const isFilterableByDateValue =
|
||||
recordFilter.type === FieldMetadataType.DATE ||
|
||||
recordFilter.type === FieldMetadataType.DATE_TIME;
|
||||
|
||||
if (isDisabled || operandHasNoInput) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isFilterableByTextValue) {
|
||||
return (
|
||||
<FormTextFieldInput
|
||||
label={''}
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={handleChange}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isDefined(subFieldNameUsedInDropdown)) {
|
||||
return (
|
||||
<AdvancedFilterValueFormCompositeFieldInput
|
||||
<WorkflowAdvancedFilterValueFormCompositeFieldInput
|
||||
recordFilter={recordFilter}
|
||||
VariablePicker={VariablePicker}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isFilterableByMultiSelectValue) {
|
||||
const metadata = fieldDefinition?.metadata as
|
||||
| FieldMultiSelectMetadata
|
||||
| FieldSelectMetadata
|
||||
| undefined;
|
||||
return (
|
||||
<FormMultiSelectFieldInput
|
||||
label={''}
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={handleChange}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
options={metadata?.options ?? []}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const field = {
|
||||
type: recordFilter.type as FieldMetadataType,
|
||||
label: '',
|
||||
@ -86,7 +140,10 @@ export const AdvancedFilterValueFormInput = ({
|
||||
field={field}
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={handleChange}
|
||||
VariablePicker={VariablePicker}
|
||||
// VariablePicker is not supported for date filters yet
|
||||
VariablePicker={
|
||||
isFilterableByDateValue ? undefined : WorkflowVariablePicker
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,44 @@
|
||||
import { AdvancedFilterLogicalOperatorDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorDropdown';
|
||||
import { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||
|
||||
import styled from '@emotion/styled';
|
||||
import { capitalize } from 'twenty-shared/utils';
|
||||
|
||||
const StyledText = styled.div`
|
||||
height: ${({ theme }) => theme.spacing(8)};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
align-items: start;
|
||||
display: flex;
|
||||
min-width: ${({ theme }) => theme.spacing(20)};
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
type WorkflowAdvancedFilterLogicalOperatorCellProps = {
|
||||
index: number;
|
||||
recordFilterGroup: RecordFilterGroup;
|
||||
};
|
||||
|
||||
export const WorkflowAdvancedFilterLogicalOperatorCell = ({
|
||||
index,
|
||||
recordFilterGroup,
|
||||
}: WorkflowAdvancedFilterLogicalOperatorCellProps) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
{index === 0 ? (
|
||||
<StyledText>Where</StyledText>
|
||||
) : index === 1 ? (
|
||||
<AdvancedFilterLogicalOperatorDropdown
|
||||
recordFilterGroup={recordFilterGroup}
|
||||
/>
|
||||
) : (
|
||||
<StyledText>
|
||||
{capitalize(recordFilterGroup.logicalOperator.toLowerCase())}
|
||||
</StyledText>
|
||||
)}
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,14 +1,13 @@
|
||||
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 { 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 styled from '@emotion/styled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -18,16 +17,14 @@ const StyledContainer = styled.div`
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const AdvancedFilterRecordFilterColumn = ({
|
||||
export const WorkflowAdvancedFilterRecordFilterColumn = ({
|
||||
recordFilterGroup,
|
||||
recordFilter,
|
||||
recordFilterIndex,
|
||||
VariablePicker,
|
||||
}: {
|
||||
recordFilterGroup: RecordFilterGroup;
|
||||
recordFilter: RecordFilter;
|
||||
recordFilterIndex: number;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
}) => {
|
||||
return (
|
||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||
@ -37,9 +34,9 @@ export const AdvancedFilterRecordFilterColumn = ({
|
||||
),
|
||||
}}
|
||||
>
|
||||
<AdvancedFilterDropdownColumn>
|
||||
<WorkflowAdvancedFilterDropdownColumn>
|
||||
<StyledContainer>
|
||||
<AdvancedFilterLogicalOperatorCell
|
||||
<WorkflowAdvancedFilterLogicalOperatorCell
|
||||
index={recordFilterIndex}
|
||||
recordFilterGroup={recordFilterGroup}
|
||||
/>
|
||||
@ -52,12 +49,12 @@ export const AdvancedFilterRecordFilterColumn = ({
|
||||
/>
|
||||
<AdvancedFilterRecordFilterOperandSelect
|
||||
recordFilterId={recordFilter.id}
|
||||
widthFromProps="auto"
|
||||
/>
|
||||
<AdvancedFilterValueFormInput
|
||||
<WorkflowAdvancedFilterValueFormInput
|
||||
recordFilterId={recordFilter.id}
|
||||
VariablePicker={VariablePicker}
|
||||
/>
|
||||
</AdvancedFilterDropdownColumn>
|
||||
</WorkflowAdvancedFilterDropdownColumn>
|
||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,56 @@
|
||||
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
|
||||
|
||||
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||
import { WorkflowAdvancedFilterRecordFilterColumn } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
|
||||
align-items: start;
|
||||
background-color: ${({ theme, isGrayBackground }) =>
|
||||
isGrayBackground ? theme.background.transparent.lighter : 'transparent'};
|
||||
border: ${({ theme }) => `1px solid ${theme.border.color.medium}`};
|
||||
border-radius: ${({ theme }) => theme.border.radius.md};
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
padding: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
type WorkflowAdvancedFilterRecordFilterGroupChildrenProps = {
|
||||
recordFilterGroupId: string;
|
||||
};
|
||||
|
||||
export const WorkflowAdvancedFilterRecordFilterGroupChildren = ({
|
||||
recordFilterGroupId,
|
||||
}: WorkflowAdvancedFilterRecordFilterGroupChildrenProps) => {
|
||||
const { currentRecordFilterGroup, childRecordFilters } =
|
||||
useChildRecordFiltersAndRecordFilterGroups({
|
||||
recordFilterGroupId,
|
||||
});
|
||||
|
||||
if (!currentRecordFilterGroup) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const hasParentRecordFilterGroup = isDefined(
|
||||
currentRecordFilterGroup.parentRecordFilterGroupId,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer isGrayBackground={hasParentRecordFilterGroup}>
|
||||
{childRecordFilters.map((childRecordFilter, childRecordFilterIndex) => (
|
||||
<WorkflowAdvancedFilterRecordFilterColumn
|
||||
key={childRecordFilter.id}
|
||||
recordFilter={childRecordFilter}
|
||||
recordFilterIndex={childRecordFilterIndex}
|
||||
recordFilterGroup={currentRecordFilterGroup}
|
||||
/>
|
||||
))}
|
||||
<AdvancedFilterAddFilterRuleSelect
|
||||
recordFilterGroup={currentRecordFilterGroup}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,9 +1,8 @@
|
||||
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 { WorkflowAdvancedFilterDropdownColumn } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterDropdownColumn';
|
||||
import { WorkflowAdvancedFilterLogicalOperatorCell } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterLogicalOperatorCell';
|
||||
import { WorkflowAdvancedFilterRecordFilterGroupChildren } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterGroupChildren';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
@ -13,21 +12,19 @@ const StyledContainer = styled.div`
|
||||
gap: ${({ theme }) => theme.spacing(1)};
|
||||
`;
|
||||
|
||||
export const AdvancedFilterRecordFilterGroupColumn = ({
|
||||
export const WorkflowAdvancedFilterRecordFilterGroupColumn = ({
|
||||
parentRecordFilterGroup,
|
||||
recordFilterGroup,
|
||||
recordFilterGroupIndex,
|
||||
VariablePicker,
|
||||
}: {
|
||||
parentRecordFilterGroup: RecordFilterGroup;
|
||||
recordFilterGroup: RecordFilterGroup;
|
||||
recordFilterGroupIndex: number;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
}) => {
|
||||
return (
|
||||
<AdvancedFilterDropdownColumn>
|
||||
<WorkflowAdvancedFilterDropdownColumn>
|
||||
<StyledContainer>
|
||||
<AdvancedFilterLogicalOperatorCell
|
||||
<WorkflowAdvancedFilterLogicalOperatorCell
|
||||
index={recordFilterGroupIndex}
|
||||
recordFilterGroup={parentRecordFilterGroup}
|
||||
/>
|
||||
@ -35,10 +32,9 @@ export const AdvancedFilterRecordFilterGroupColumn = ({
|
||||
recordFilterGroupId={recordFilterGroup.id}
|
||||
/>
|
||||
</StyledContainer>
|
||||
<AdvancedFilterRecordFilterGroupChildren
|
||||
<WorkflowAdvancedFilterRecordFilterGroupChildren
|
||||
recordFilterGroupId={recordFilterGroup.id}
|
||||
VariablePicker={VariablePicker}
|
||||
/>
|
||||
</AdvancedFilterDropdownColumn>
|
||||
</WorkflowAdvancedFilterDropdownColumn>
|
||||
);
|
||||
};
|
||||
@ -3,19 +3,17 @@ import { FormCountryCodeSelectInput } from '@/object-record/record-field/form-ty
|
||||
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 { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
import { JsonValue } from 'type-fest';
|
||||
|
||||
export const AdvancedFilterValueFormCompositeFieldInput = ({
|
||||
export const WorkflowAdvancedFilterValueFormCompositeFieldInput = ({
|
||||
recordFilter,
|
||||
onChange,
|
||||
VariablePicker,
|
||||
}: {
|
||||
recordFilter: RecordFilter;
|
||||
onChange: (newValue: JsonValue) => void;
|
||||
VariablePicker?: VariablePickerComponent;
|
||||
}) => {
|
||||
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
|
||||
subFieldNameUsedInDropdownComponentState,
|
||||
@ -30,13 +28,13 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
||||
<FormCountrySelectInput
|
||||
selectedCountryName={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
) : (
|
||||
<FormTextFieldInput
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
)
|
||||
) : filterType === 'CURRENCY' ? (
|
||||
@ -44,13 +42,13 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
||||
<FormCountryCodeSelectInput
|
||||
selectedCountryCode={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
) : recordFilter.subFieldName === 'amountMicros' ? (
|
||||
<FormNumberFieldInput
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
) : null
|
||||
) : filterType === 'PHONES' ? (
|
||||
@ -58,20 +56,20 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
||||
<FormNumberFieldInput
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
) : (
|
||||
<FormTextFieldInput
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<FormTextFieldInput
|
||||
defaultValue={recordFilter.value}
|
||||
onChange={onChange}
|
||||
VariablePicker={VariablePicker}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@ -1,8 +1,6 @@
|
||||
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
|
||||
import { AdvancedFilterRecordFilterColumn } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterColumn';
|
||||
import { AdvancedFilterRecordFilterGroupColumn } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupColumn';
|
||||
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
|
||||
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
|
||||
@ -13,9 +11,10 @@ import { computeRecordGqlOperationFilter } from '@/object-record/record-filter/u
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { WorkflowAdvancedFilterRecordFilterColumn } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterColumn';
|
||||
import { WorkflowAdvancedFilterRecordFilterGroupColumn } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowAdvancedFilterRecordFilterGroupColumn';
|
||||
import { FindRecordsActionFilter } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowEditActionFindRecords';
|
||||
import { WorkflowFindRecordsAddFilterButton } from '@/workflow/workflow-steps/workflow-actions/find-records-action/components/WorkflowFindRecordsAddFilterButton';
|
||||
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@ -110,7 +109,7 @@ export const WorkflowFindRecordsFilters = ({
|
||||
<AdvancedFilterContext.Provider
|
||||
value={{
|
||||
onUpdate,
|
||||
isColumn: true,
|
||||
isWorkflowFindRecords: true,
|
||||
}}
|
||||
>
|
||||
{isDefined(rootRecordFilterGroup) ? (
|
||||
@ -121,20 +120,18 @@ export const WorkflowFindRecordsFilters = ({
|
||||
isRecordFilterGroupChildARecordFilterGroup(
|
||||
recordFilterGroupChild,
|
||||
) ? (
|
||||
<AdvancedFilterRecordFilterGroupColumn
|
||||
<WorkflowAdvancedFilterRecordFilterGroupColumn
|
||||
key={recordFilterGroupChild.id}
|
||||
parentRecordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilterGroup={recordFilterGroupChild}
|
||||
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
) : (
|
||||
<AdvancedFilterRecordFilterColumn
|
||||
<WorkflowAdvancedFilterRecordFilterColumn
|
||||
key={recordFilterGroupChild.id}
|
||||
recordFilterGroup={rootRecordFilterGroup}
|
||||
recordFilter={recordFilterGroupChild}
|
||||
recordFilterIndex={recordFilterGroupChildIndex}
|
||||
VariablePicker={WorkflowVariablePicker}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
|
||||
@ -2,7 +2,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
||||
import { WorkflowActionType } from '@/workflow/types/Workflow';
|
||||
import { FieldMetadataType } from '~/generated/graphql';
|
||||
|
||||
const DISPLAYABLE_FIELD_TYPES_FOR_UPDATE = [
|
||||
const COMMON_DISPLAYABLE_FIELD_TYPES = [
|
||||
FieldMetadataType.TEXT,
|
||||
FieldMetadataType.NUMBER,
|
||||
FieldMetadataType.DATE,
|
||||
@ -20,6 +20,12 @@ const DISPLAYABLE_FIELD_TYPES_FOR_UPDATE = [
|
||||
FieldMetadataType.UUID,
|
||||
];
|
||||
|
||||
const FIND_RECORDS_DISPLAYABLE_FIELD_TYPES = [
|
||||
...COMMON_DISPLAYABLE_FIELD_TYPES,
|
||||
FieldMetadataType.ARRAY,
|
||||
FieldMetadataType.RELATION,
|
||||
];
|
||||
|
||||
export const shouldDisplayFormField = ({
|
||||
fieldMetadataItem,
|
||||
actionType,
|
||||
@ -37,9 +43,14 @@ export const shouldDisplayFormField = ({
|
||||
break;
|
||||
case 'UPDATE_RECORD':
|
||||
isTypeAllowedForAction =
|
||||
DISPLAYABLE_FIELD_TYPES_FOR_UPDATE.includes(fieldMetadataItem.type) ||
|
||||
COMMON_DISPLAYABLE_FIELD_TYPES.includes(fieldMetadataItem.type) ||
|
||||
fieldMetadataItem.settings?.['relationType'] === 'MANY_TO_ONE';
|
||||
break;
|
||||
case 'FIND_RECORDS':
|
||||
isTypeAllowedForAction = FIND_RECORDS_DISPLAYABLE_FIELD_TYPES.includes(
|
||||
fieldMetadataItem.type,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Action "${actionType}" is not supported`);
|
||||
}
|
||||
|
||||
@ -26,4 +26,5 @@ export { isValidUrl } from './url/isValidUrl';
|
||||
export { isDefined } from './validation/isDefined';
|
||||
export { isValidLocale } from './validation/isValidLocale';
|
||||
export { isValidUuid } from './validation/isValidUuid';
|
||||
export { isValidVariable } from './validation/isValidVariable';
|
||||
export { normalizeLocale } from './validation/normalizeLocale';
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
export const isValidVariable = (variable: string): boolean => {
|
||||
return /^{{[^{}]+}}$/.test(variable);
|
||||
};
|
||||
Reference in New Issue
Block a user