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 { 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 { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useContext } from 'react';
|
|
||||||
import { capitalize } from 'twenty-shared/utils';
|
import { capitalize } from 'twenty-shared/utils';
|
||||||
|
|
||||||
const StyledText = styled.div<{ noPadding?: boolean }>`
|
const StyledText = styled.div`
|
||||||
height: ${({ theme }) => theme.spacing(8)};
|
height: ${({ theme }) => theme.spacing(8)};
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
padding-left: ${({ theme, noPadding }) =>
|
padding-left: ${({ theme }) => theme.spacing(2.25)};
|
||||||
noPadding ? 0 : theme.spacing(2.25)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -31,18 +28,16 @@ export const AdvancedFilterLogicalOperatorCell = ({
|
|||||||
index,
|
index,
|
||||||
recordFilterGroup,
|
recordFilterGroup,
|
||||||
}: AdvancedFilterLogicalOperatorCellProps) => {
|
}: AdvancedFilterLogicalOperatorCellProps) => {
|
||||||
const { isColumn } = useContext(AdvancedFilterContext);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
{index === 0 ? (
|
{index === 0 ? (
|
||||||
<StyledText noPadding={isColumn}>Where</StyledText>
|
<StyledText>Where</StyledText>
|
||||||
) : index === 1 ? (
|
) : index === 1 ? (
|
||||||
<AdvancedFilterLogicalOperatorDropdown
|
<AdvancedFilterLogicalOperatorDropdown
|
||||||
recordFilterGroup={recordFilterGroup}
|
recordFilterGroup={recordFilterGroup}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<StyledText noPadding={isColumn}>
|
<StyledText>
|
||||||
{capitalize(recordFilterGroup.logicalOperator.toLowerCase())}
|
{capitalize(recordFilterGroup.logicalOperator.toLowerCase())}
|
||||||
</StyledText>
|
</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 { 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 { 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 styled from '@emotion/styled';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
@ -17,17 +16,14 @@ const StyledContainer = styled.div<{ isGrayBackground?: boolean }>`
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: ${({ theme }) => theme.spacing(2)};
|
gap: ${({ theme }) => theme.spacing(2)};
|
||||||
padding: ${({ theme }) => theme.spacing(2)};
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
overflow: hidden;
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type AdvancedFilterRecordFilterGroupChildrenProps = {
|
type AdvancedFilterRecordFilterGroupChildrenProps = {
|
||||||
recordFilterGroupId: string;
|
recordFilterGroupId: string;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdvancedFilterRecordFilterGroupChildren = ({
|
export const AdvancedFilterRecordFilterGroupChildren = ({
|
||||||
recordFilterGroupId,
|
recordFilterGroupId,
|
||||||
VariablePicker,
|
|
||||||
}: AdvancedFilterRecordFilterGroupChildrenProps) => {
|
}: AdvancedFilterRecordFilterGroupChildrenProps) => {
|
||||||
const { currentRecordFilterGroup, childRecordFilters } =
|
const { currentRecordFilterGroup, childRecordFilters } =
|
||||||
useChildRecordFiltersAndRecordFilterGroups({
|
useChildRecordFiltersAndRecordFilterGroups({
|
||||||
@ -45,12 +41,11 @@ export const AdvancedFilterRecordFilterGroupChildren = ({
|
|||||||
return (
|
return (
|
||||||
<StyledContainer isGrayBackground={hasParentRecordFilterGroup}>
|
<StyledContainer isGrayBackground={hasParentRecordFilterGroup}>
|
||||||
{childRecordFilters.map((childRecordFilter, childRecordFilterIndex) => (
|
{childRecordFilters.map((childRecordFilter, childRecordFilterIndex) => (
|
||||||
<AdvancedFilterRecordFilter
|
<AdvancedFilterRecordFilterRow
|
||||||
key={childRecordFilter.id}
|
key={childRecordFilter.id}
|
||||||
recordFilter={childRecordFilter}
|
recordFilter={childRecordFilter}
|
||||||
recordFilterIndex={childRecordFilterIndex}
|
recordFilterIndex={childRecordFilterIndex}
|
||||||
recordFilterGroup={currentRecordFilterGroup}
|
recordFilterGroup={currentRecordFilterGroup}
|
||||||
VariablePicker={VariablePicker}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<AdvancedFilterAddFilterRuleSelect
|
<AdvancedFilterAddFilterRuleSelect
|
||||||
|
|||||||
@ -2,19 +2,16 @@ import { AdvancedFilterDropdownRow } from '@/object-record/advanced-filter/compo
|
|||||||
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
|
import { AdvancedFilterLogicalOperatorCell } from '@/object-record/advanced-filter/components/AdvancedFilterLogicalOperatorCell';
|
||||||
import { AdvancedFilterRecordFilterGroupChildren } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupChildren';
|
import { AdvancedFilterRecordFilterGroupChildren } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupChildren';
|
||||||
import { AdvancedFilterRecordFilterGroupOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupOptionsDropdown';
|
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 { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
|
|
||||||
export const AdvancedFilterRecordFilterGroupRow = ({
|
export const AdvancedFilterRecordFilterGroupRow = ({
|
||||||
parentRecordFilterGroup,
|
parentRecordFilterGroup,
|
||||||
recordFilterGroup,
|
recordFilterGroup,
|
||||||
recordFilterGroupIndex,
|
recordFilterGroupIndex,
|
||||||
VariablePicker,
|
|
||||||
}: {
|
}: {
|
||||||
parentRecordFilterGroup: RecordFilterGroup;
|
parentRecordFilterGroup: RecordFilterGroup;
|
||||||
recordFilterGroup: RecordFilterGroup;
|
recordFilterGroup: RecordFilterGroup;
|
||||||
recordFilterGroupIndex: number;
|
recordFilterGroupIndex: number;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<AdvancedFilterDropdownRow>
|
<AdvancedFilterDropdownRow>
|
||||||
@ -24,7 +21,6 @@ export const AdvancedFilterRecordFilterGroupRow = ({
|
|||||||
/>
|
/>
|
||||||
<AdvancedFilterRecordFilterGroupChildren
|
<AdvancedFilterRecordFilterGroupChildren
|
||||||
recordFilterGroupId={recordFilterGroup.id}
|
recordFilterGroupId={recordFilterGroup.id}
|
||||||
VariablePicker={VariablePicker}
|
|
||||||
/>
|
/>
|
||||||
<AdvancedFilterRecordFilterGroupOptionsDropdown
|
<AdvancedFilterRecordFilterGroupOptionsDropdown
|
||||||
recordFilterGroupId={recordFilterGroup.id}
|
recordFilterGroupId={recordFilterGroup.id}
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
|
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 { useApplyObjectFilterDropdownOperand } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownOperand';
|
||||||
|
|
||||||
import { getOperandLabel } from '@/object-record/object-filter-dropdown/utils/getOperandLabel';
|
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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useContext } from 'react';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { MenuItem } from 'twenty-ui/navigation';
|
import { MenuItem } from 'twenty-ui/navigation';
|
||||||
|
|
||||||
const StyledContainer = styled.div<{ isColumn?: boolean }>`
|
const StyledContainer = styled.div<{ width?: string }>`
|
||||||
width: ${({ isColumn }) => (isColumn ? 'auto' : '100px')};
|
width: ${({ width }) => width ?? '100px'};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
type AdvancedFilterRecordFilterOperandSelectProps = {
|
type AdvancedFilterRecordFilterOperandSelectProps = {
|
||||||
recordFilterId: string;
|
recordFilterId: string;
|
||||||
|
widthFromProps?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdvancedFilterRecordFilterOperandSelect = ({
|
export const AdvancedFilterRecordFilterOperandSelect = ({
|
||||||
recordFilterId,
|
recordFilterId,
|
||||||
|
widthFromProps,
|
||||||
}: AdvancedFilterRecordFilterOperandSelectProps) => {
|
}: AdvancedFilterRecordFilterOperandSelectProps) => {
|
||||||
const dropdownId = `advanced-filter-view-filter-operand-${recordFilterId}`;
|
const dropdownId = `advanced-filter-view-filter-operand-${recordFilterId}`;
|
||||||
|
|
||||||
@ -38,8 +38,6 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
|||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
);
|
);
|
||||||
|
|
||||||
const { isColumn } = useContext(AdvancedFilterContext);
|
|
||||||
|
|
||||||
const filter = currentRecordFilters.find(
|
const filter = currentRecordFilters.find(
|
||||||
(recordFilter) => recordFilter.id === recordFilterId,
|
(recordFilter) => recordFilter.id === recordFilterId,
|
||||||
);
|
);
|
||||||
@ -86,7 +84,7 @@ export const AdvancedFilterRecordFilterOperandSelect = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledContainer isColumn={isColumn}>
|
<StyledContainer width={widthFromProps}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
|
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
|
||||||
import { AdvancedFilterRecordFilter } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilter';
|
import { AdvancedFilterRecordFilterGroupRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroupRow';
|
||||||
import { AdvancedFilterRecordFilterGroup } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterGroup';
|
import { AdvancedFilterRecordFilterRow } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterRow';
|
||||||
|
|
||||||
import { ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth';
|
import { ADVANCED_FILTER_DROPDOWN_CONTENT_WIDTH } from '@/object-record/advanced-filter/constants/AdvancedFilterDropdownContentWidth';
|
||||||
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
import { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||||
@ -45,14 +45,14 @@ export const AdvancedFilterRootRecordFilterGroup = () => {
|
|||||||
isRecordFilterGroupChildARecordFilterGroup(
|
isRecordFilterGroupChildARecordFilterGroup(
|
||||||
recordFilterGroupChild,
|
recordFilterGroupChild,
|
||||||
) ? (
|
) ? (
|
||||||
<AdvancedFilterRecordFilterGroup
|
<AdvancedFilterRecordFilterGroupRow
|
||||||
key={recordFilterGroupChild.id}
|
key={recordFilterGroupChild.id}
|
||||||
parentRecordFilterGroup={rootRecordFilterGroup}
|
parentRecordFilterGroup={rootRecordFilterGroup}
|
||||||
recordFilterGroup={recordFilterGroupChild}
|
recordFilterGroup={recordFilterGroupChild}
|
||||||
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<AdvancedFilterRecordFilter
|
<AdvancedFilterRecordFilterRow
|
||||||
key={recordFilterGroupChild.id}
|
key={recordFilterGroupChild.id}
|
||||||
recordFilterGroup={rootRecordFilterGroup}
|
recordFilterGroup={rootRecordFilterGroup}
|
||||||
recordFilter={recordFilterGroupChild}
|
recordFilter={recordFilterGroupChild}
|
||||||
|
|||||||
@ -2,14 +2,12 @@ import { AdvancedFilterDropdownFilterInput } from '@/object-record/advanced-filt
|
|||||||
import { AdvancedFilterDropdownTextInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput';
|
import { AdvancedFilterDropdownTextInput } from '@/object-record/advanced-filter/components/AdvancedFilterDropdownTextInput';
|
||||||
import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect';
|
import { AdvancedFilterValueInputDropdownButtonClickableSelect } from '@/object-record/advanced-filter/components/AdvancedFilterValueInputDropdownButtonClickableSelect';
|
||||||
import { DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET } from '@/object-record/advanced-filter/constants/DefaultAdvancedFilterDropdownOffset';
|
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 { shouldShowFilterTextInput } from '@/object-record/advanced-filter/utils/shouldShowFilterTextInput';
|
||||||
import { TEXT_FILTER_TYPES } from '@/object-record/object-filter-dropdown/constants/TextFilterTypes';
|
|
||||||
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
import { fieldMetadataItemIdUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemIdUsedInDropdownComponentState';
|
||||||
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
import { objectFilterDropdownCurrentRecordFilterComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownCurrentRecordFilterComponentState';
|
||||||
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState';
|
||||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
||||||
import { configurableViewFilterOperands } from '@/object-record/object-filter-dropdown/utils/configurableViewFilterOperands';
|
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 { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
import { DropdownOffset } from '@/ui/layout/dropdown/types/DropdownOffset';
|
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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
const StyledValueDropdownContainer = styled.div`
|
const StyledValueDropdownContainer = styled.div`
|
||||||
@ -83,31 +80,10 @@ export const AdvancedFilterValueInput = ({
|
|||||||
? ({ y: -33, x: 0 } satisfies DropdownOffset)
|
? ({ y: -33, x: 0 } satisfies DropdownOffset)
|
||||||
: DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET;
|
: DEFAULT_ADVANCED_FILTER_DROPDOWN_OFFSET;
|
||||||
|
|
||||||
const isFilterableByTextValue =
|
const showFilterTextInputInsteadOfDropdown = shouldShowFilterTextInput({
|
||||||
isDefined(filterType) &&
|
recordFilter,
|
||||||
(TEXT_FILTER_TYPES.includes(filterType) ||
|
subFieldNameUsedInDropdown,
|
||||||
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;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledValueDropdownContainer>
|
<StyledValueDropdownContainer>
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { createContext } from 'react';
|
|||||||
|
|
||||||
type AdvancedFilterContextType = {
|
type AdvancedFilterContextType = {
|
||||||
onUpdate?: () => void;
|
onUpdate?: () => void;
|
||||||
isColumn?: boolean;
|
isWorkflowFindRecords?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AdvancedFilterContext = createContext<AdvancedFilterContextType>(
|
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 { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { RelationFilterValue } from '@/views/view-filter-value/types/RelationFilterValue';
|
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 { 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 { isDefined } from 'twenty-shared/utils';
|
||||||
import { IconUserCircle } from 'twenty-ui/display';
|
import { IconUserCircle } from 'twenty-ui/display';
|
||||||
|
|
||||||
@ -59,7 +59,7 @@ export const ObjectFilterDropdownRecordSelect = ({
|
|||||||
const { isCurrentWorkspaceMemberSelected } = jsonRelationFilterValueSchema
|
const { isCurrentWorkspaceMemberSelected } = jsonRelationFilterValueSchema
|
||||||
.catch({
|
.catch({
|
||||||
isCurrentWorkspaceMemberSelected: false,
|
isCurrentWorkspaceMemberSelected: false,
|
||||||
selectedRecordIds: simpleRelationFilterValueSchema.parse(
|
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
|
||||||
objectFilterDropdownFilterValue,
|
objectFilterDropdownFilterValue,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
@ -105,7 +105,7 @@ export const ObjectFilterDropdownRecordSelect = ({
|
|||||||
const { selectedRecordIds } = jsonRelationFilterValueSchema
|
const { selectedRecordIds } = jsonRelationFilterValueSchema
|
||||||
.catch({
|
.catch({
|
||||||
isCurrentWorkspaceMemberSelected: false,
|
isCurrentWorkspaceMemberSelected: false,
|
||||||
selectedRecordIds: simpleRelationFilterValueSchema.parse(
|
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
|
||||||
recordFilterUsedInDropdown?.value,
|
recordFilterUsedInDropdown?.value,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,11 +12,13 @@ export type FormCountryCodeSelectInputUpdatedValue = CountryCode | '';
|
|||||||
export const FormCountryCodeSelectInput = ({
|
export const FormCountryCodeSelectInput = ({
|
||||||
selectedCountryCode,
|
selectedCountryCode,
|
||||||
onChange,
|
onChange,
|
||||||
|
label,
|
||||||
readonly = false,
|
readonly = false,
|
||||||
VariablePicker,
|
VariablePicker,
|
||||||
}: {
|
}: {
|
||||||
selectedCountryCode: string;
|
selectedCountryCode: string;
|
||||||
onChange: (countryCode: FormCountryCodeSelectInputUpdatedValue) => void;
|
onChange: (countryCode: FormCountryCodeSelectInputUpdatedValue) => void;
|
||||||
|
label?: string;
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
VariablePicker?: VariablePickerComponent;
|
VariablePicker?: VariablePickerComponent;
|
||||||
}) => {
|
}) => {
|
||||||
@ -55,7 +57,7 @@ export const FormCountryCodeSelectInput = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormSelectFieldInput
|
<FormSelectFieldInput
|
||||||
label="Country Code"
|
label={label}
|
||||||
onChange={onCountryCodeChange}
|
onChange={onCountryCodeChange}
|
||||||
options={options}
|
options={options}
|
||||||
defaultValue={selectedCountryCode}
|
defaultValue={selectedCountryCode}
|
||||||
|
|||||||
@ -55,13 +55,14 @@ export const FormPhoneFieldInput = ({
|
|||||||
{label && <InputLabel>{label}</InputLabel>}
|
{label && <InputLabel>{label}</InputLabel>}
|
||||||
<FormNestedFieldInputContainer>
|
<FormNestedFieldInputContainer>
|
||||||
<FormCountryCodeSelectInput
|
<FormCountryCodeSelectInput
|
||||||
selectedCountryCode={defaultValue?.primaryPhoneCountryCode || ''}
|
label="Country Code"
|
||||||
|
selectedCountryCode={defaultValue?.primaryPhoneCountryCode ?? ''}
|
||||||
onChange={handleCountryChange}
|
onChange={handleCountryChange}
|
||||||
readonly={readonly}
|
readonly={readonly}
|
||||||
/>
|
/>
|
||||||
<FormNumberFieldInput
|
<FormNumberFieldInput
|
||||||
label="Phone Number"
|
label="Phone Number"
|
||||||
defaultValue={defaultValue?.primaryPhoneNumber || ''}
|
defaultValue={defaultValue?.primaryPhoneNumber ?? ''}
|
||||||
onChange={handleNumberChange}
|
onChange={handleNumberChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={VariablePicker}
|
||||||
placeholder="Enter phone number"
|
placeholder="Enter phone number"
|
||||||
|
|||||||
@ -6,7 +6,9 @@ import { FormCountryCodeSelectInput } from '../FormCountryCodeSelectInput';
|
|||||||
const meta: Meta<typeof FormCountryCodeSelectInput> = {
|
const meta: Meta<typeof FormCountryCodeSelectInput> = {
|
||||||
title: 'UI/Data/Field/Form/Input/FormCountryCodeSelectInput',
|
title: 'UI/Data/Field/Form/Input/FormCountryCodeSelectInput',
|
||||||
component: FormCountryCodeSelectInput,
|
component: FormCountryCodeSelectInput,
|
||||||
args: {},
|
args: {
|
||||||
|
label: 'Country Code',
|
||||||
|
},
|
||||||
argTypes: {},
|
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 { useFilterableFieldMetadataItems } from '@/object-record/record-filter/hooks/useFilterableFieldMetadataItems';
|
||||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
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 = () => {
|
export const useFilterableFieldMetadataItemsInRecordIndexContext = () => {
|
||||||
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
const { objectMetadataItem } = useRecordIndexContextOrThrow();
|
||||||
|
const { isWorkflowFindRecords } = useContext(AdvancedFilterContext);
|
||||||
|
|
||||||
const { filterableFieldMetadataItems } = useFilterableFieldMetadataItems(
|
const {
|
||||||
objectMetadataItem.id,
|
filterableFieldMetadataItems: filterableFieldMetadataItemsForRecordIndex,
|
||||||
);
|
} = useFilterableFieldMetadataItems(objectMetadataItem.id);
|
||||||
|
|
||||||
|
const filterableFieldMetadataItems = isWorkflowFindRecords
|
||||||
|
? filterableFieldMetadataItemsForRecordIndex.filter((fieldMetadataItem) =>
|
||||||
|
shouldDisplayFormField({
|
||||||
|
fieldMetadataItem,
|
||||||
|
actionType: 'FIND_RECORDS',
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
: filterableFieldMetadataItemsForRecordIndex;
|
||||||
|
|
||||||
return { filterableFieldMetadataItems };
|
return { filterableFieldMetadataItems };
|
||||||
};
|
};
|
||||||
|
|||||||
@ -33,9 +33,7 @@ import { RecordFilterValueDependencies } from '@/object-record/record-filter/typ
|
|||||||
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
|
import { getEmptyRecordGqlOperationFilter } from '@/object-record/record-filter/utils/getEmptyRecordGqlOperationFilter';
|
||||||
|
|
||||||
import { resolveDateViewFilterValue } from '@/views/view-filter-value/utils/resolveDateViewFilterValue';
|
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 { 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 { endOfDay, roundToNearestMinutes, startOfDay } from 'date-fns';
|
||||||
import { z } from 'zod';
|
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 { 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 { 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 { 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 { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
|||||||
jsonRelationFilterValueSchema
|
jsonRelationFilterValueSchema
|
||||||
.catch({
|
.catch({
|
||||||
isCurrentWorkspaceMemberSelected: false,
|
isCurrentWorkspaceMemberSelected: false,
|
||||||
selectedRecordIds: simpleRelationFilterValueSchema.parse(
|
selectedRecordIds: arrayOfUuidOrVariableSchema.parse(
|
||||||
recordFilter.value,
|
recordFilter.value,
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
@ -759,7 +759,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
case 'MULTI_SELECT': {
|
case 'MULTI_SELECT': {
|
||||||
const options = resolveSelectViewFilterValue(recordFilter);
|
const options = arrayOfStringsOrVariablesSchema.parse(recordFilter.value);
|
||||||
|
|
||||||
if (options.length === 0) return;
|
if (options.length === 0) return;
|
||||||
|
|
||||||
@ -817,7 +817,7 @@ export const turnRecordFilterIntoRecordGqlOperationFilter = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'SELECT': {
|
case 'SELECT': {
|
||||||
const options = resolveSelectViewFilterValue(recordFilter);
|
const options = arrayOfStringsOrVariablesSchema.parse(recordFilter.value);
|
||||||
|
|
||||||
if (options.length === 0) return;
|
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)};
|
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 { 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 { useApplyObjectFilterDropdownFilterValue } from '@/object-record/object-filter-dropdown/hooks/useApplyObjectFilterDropdownFilterValue';
|
||||||
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
import { fieldMetadataItemUsedInDropdownComponentSelector } from '@/object-record/object-filter-dropdown/states/fieldMetadataItemUsedInDropdownComponentSelector';
|
||||||
import { subFieldNameUsedInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/subFieldNameUsedInDropdownComponentState';
|
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 { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
|
||||||
import { VariablePickerComponent } from '@/object-record/record-field/form-types/types/VariablePickerComponent';
|
import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-types/components/FormMultiSelectFieldInput';
|
||||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
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 { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState';
|
||||||
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
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 { isObject } from '@sniptt/guards';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
import { JsonValue } from 'type-fest';
|
import { JsonValue } from 'type-fest';
|
||||||
|
|
||||||
export const AdvancedFilterValueFormInput = ({
|
export const WorkflowAdvancedFilterValueFormInput = ({
|
||||||
recordFilterId,
|
recordFilterId,
|
||||||
VariablePicker,
|
|
||||||
}: {
|
}: {
|
||||||
recordFilterId: string;
|
recordFilterId: string;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
}) => {
|
}) => {
|
||||||
const currentRecordFilters = useRecoilComponentValueV2(
|
const currentRecordFilters = useRecoilComponentValueV2(
|
||||||
currentRecordFiltersComponentState,
|
currentRecordFiltersComponentState,
|
||||||
@ -37,6 +43,11 @@ export const AdvancedFilterValueFormInput = ({
|
|||||||
|
|
||||||
const isDisabled = !recordFilter?.fieldMetadataId || !recordFilter.operand;
|
const isDisabled = !recordFilter?.fieldMetadataId || !recordFilter.operand;
|
||||||
|
|
||||||
|
const operandHasNoInput =
|
||||||
|
(recordFilter &&
|
||||||
|
!configurableViewFilterOperands.has(recordFilter.operand)) ??
|
||||||
|
true;
|
||||||
|
|
||||||
const { applyObjectFilterDropdownFilterValue } =
|
const { applyObjectFilterDropdownFilterValue } =
|
||||||
useApplyObjectFilterDropdownFilterValue();
|
useApplyObjectFilterDropdownFilterValue();
|
||||||
|
|
||||||
@ -61,20 +72,63 @@ export const AdvancedFilterValueFormInput = ({
|
|||||||
})
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (isDisabled) {
|
if (!isDefined(recordFilter)) {
|
||||||
return null;
|
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)) {
|
if (isDefined(subFieldNameUsedInDropdown)) {
|
||||||
return (
|
return (
|
||||||
<AdvancedFilterValueFormCompositeFieldInput
|
<WorkflowAdvancedFilterValueFormCompositeFieldInput
|
||||||
recordFilter={recordFilter}
|
recordFilter={recordFilter}
|
||||||
VariablePicker={VariablePicker}
|
|
||||||
onChange={handleChange}
|
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 = {
|
const field = {
|
||||||
type: recordFilter.type as FieldMetadataType,
|
type: recordFilter.type as FieldMetadataType,
|
||||||
label: '',
|
label: '',
|
||||||
@ -86,7 +140,10 @@ export const AdvancedFilterValueFormInput = ({
|
|||||||
field={field}
|
field={field}
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={handleChange}
|
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 { 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 { AdvancedFilterRecordFilterOperandSelect } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOperandSelect';
|
||||||
import { AdvancedFilterRecordFilterOptionsDropdown } from '@/object-record/advanced-filter/components/AdvancedFilterRecordFilterOptionsDropdown';
|
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 { getAdvancedFilterObjectFilterDropdownComponentInstanceId } from '@/object-record/advanced-filter/utils/getAdvancedFilterObjectFilterDropdownComponentInstanceId';
|
||||||
import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext';
|
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 { RecordFilterGroup } from '@/object-record/record-filter-group/types/RecordFilterGroup';
|
||||||
import { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
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';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -18,16 +17,14 @@ const StyledContainer = styled.div`
|
|||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const AdvancedFilterRecordFilterColumn = ({
|
export const WorkflowAdvancedFilterRecordFilterColumn = ({
|
||||||
recordFilterGroup,
|
recordFilterGroup,
|
||||||
recordFilter,
|
recordFilter,
|
||||||
recordFilterIndex,
|
recordFilterIndex,
|
||||||
VariablePicker,
|
|
||||||
}: {
|
}: {
|
||||||
recordFilterGroup: RecordFilterGroup;
|
recordFilterGroup: RecordFilterGroup;
|
||||||
recordFilter: RecordFilter;
|
recordFilter: RecordFilter;
|
||||||
recordFilterIndex: number;
|
recordFilterIndex: number;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<ObjectFilterDropdownComponentInstanceContext.Provider
|
<ObjectFilterDropdownComponentInstanceContext.Provider
|
||||||
@ -37,9 +34,9 @@ export const AdvancedFilterRecordFilterColumn = ({
|
|||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AdvancedFilterDropdownColumn>
|
<WorkflowAdvancedFilterDropdownColumn>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<AdvancedFilterLogicalOperatorCell
|
<WorkflowAdvancedFilterLogicalOperatorCell
|
||||||
index={recordFilterIndex}
|
index={recordFilterIndex}
|
||||||
recordFilterGroup={recordFilterGroup}
|
recordFilterGroup={recordFilterGroup}
|
||||||
/>
|
/>
|
||||||
@ -52,12 +49,12 @@ export const AdvancedFilterRecordFilterColumn = ({
|
|||||||
/>
|
/>
|
||||||
<AdvancedFilterRecordFilterOperandSelect
|
<AdvancedFilterRecordFilterOperandSelect
|
||||||
recordFilterId={recordFilter.id}
|
recordFilterId={recordFilter.id}
|
||||||
|
widthFromProps="auto"
|
||||||
/>
|
/>
|
||||||
<AdvancedFilterValueFormInput
|
<WorkflowAdvancedFilterValueFormInput
|
||||||
recordFilterId={recordFilter.id}
|
recordFilterId={recordFilter.id}
|
||||||
VariablePicker={VariablePicker}
|
|
||||||
/>
|
/>
|
||||||
</AdvancedFilterDropdownColumn>
|
</WorkflowAdvancedFilterDropdownColumn>
|
||||||
</ObjectFilterDropdownComponentInstanceContext.Provider>
|
</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 { 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 { 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';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
const StyledContainer = styled.div`
|
const StyledContainer = styled.div`
|
||||||
@ -13,21 +12,19 @@ const StyledContainer = styled.div`
|
|||||||
gap: ${({ theme }) => theme.spacing(1)};
|
gap: ${({ theme }) => theme.spacing(1)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const AdvancedFilterRecordFilterGroupColumn = ({
|
export const WorkflowAdvancedFilterRecordFilterGroupColumn = ({
|
||||||
parentRecordFilterGroup,
|
parentRecordFilterGroup,
|
||||||
recordFilterGroup,
|
recordFilterGroup,
|
||||||
recordFilterGroupIndex,
|
recordFilterGroupIndex,
|
||||||
VariablePicker,
|
|
||||||
}: {
|
}: {
|
||||||
parentRecordFilterGroup: RecordFilterGroup;
|
parentRecordFilterGroup: RecordFilterGroup;
|
||||||
recordFilterGroup: RecordFilterGroup;
|
recordFilterGroup: RecordFilterGroup;
|
||||||
recordFilterGroupIndex: number;
|
recordFilterGroupIndex: number;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<AdvancedFilterDropdownColumn>
|
<WorkflowAdvancedFilterDropdownColumn>
|
||||||
<StyledContainer>
|
<StyledContainer>
|
||||||
<AdvancedFilterLogicalOperatorCell
|
<WorkflowAdvancedFilterLogicalOperatorCell
|
||||||
index={recordFilterGroupIndex}
|
index={recordFilterGroupIndex}
|
||||||
recordFilterGroup={parentRecordFilterGroup}
|
recordFilterGroup={parentRecordFilterGroup}
|
||||||
/>
|
/>
|
||||||
@ -35,10 +32,9 @@ export const AdvancedFilterRecordFilterGroupColumn = ({
|
|||||||
recordFilterGroupId={recordFilterGroup.id}
|
recordFilterGroupId={recordFilterGroup.id}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
<AdvancedFilterRecordFilterGroupChildren
|
<WorkflowAdvancedFilterRecordFilterGroupChildren
|
||||||
recordFilterGroupId={recordFilterGroup.id}
|
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 { FormCountrySelectInput } from '@/object-record/record-field/form-types/components/FormCountrySelectInput';
|
||||||
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
|
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
|
||||||
import { FormTextFieldInput } from '@/object-record/record-field/form-types/components/FormTextFieldInput';
|
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 { RecordFilter } from '@/object-record/record-filter/types/RecordFilter';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
|
||||||
import { JsonValue } from 'type-fest';
|
import { JsonValue } from 'type-fest';
|
||||||
|
|
||||||
export const AdvancedFilterValueFormCompositeFieldInput = ({
|
export const WorkflowAdvancedFilterValueFormCompositeFieldInput = ({
|
||||||
recordFilter,
|
recordFilter,
|
||||||
onChange,
|
onChange,
|
||||||
VariablePicker,
|
|
||||||
}: {
|
}: {
|
||||||
recordFilter: RecordFilter;
|
recordFilter: RecordFilter;
|
||||||
onChange: (newValue: JsonValue) => void;
|
onChange: (newValue: JsonValue) => void;
|
||||||
VariablePicker?: VariablePickerComponent;
|
|
||||||
}) => {
|
}) => {
|
||||||
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
|
const subFieldNameUsedInDropdown = useRecoilComponentValueV2(
|
||||||
subFieldNameUsedInDropdownComponentState,
|
subFieldNameUsedInDropdownComponentState,
|
||||||
@ -30,13 +28,13 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
|||||||
<FormCountrySelectInput
|
<FormCountrySelectInput
|
||||||
selectedCountryName={recordFilter.value}
|
selectedCountryName={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FormTextFieldInput
|
<FormTextFieldInput
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : filterType === 'CURRENCY' ? (
|
) : filterType === 'CURRENCY' ? (
|
||||||
@ -44,13 +42,13 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
|||||||
<FormCountryCodeSelectInput
|
<FormCountryCodeSelectInput
|
||||||
selectedCountryCode={recordFilter.value}
|
selectedCountryCode={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
) : recordFilter.subFieldName === 'amountMicros' ? (
|
) : recordFilter.subFieldName === 'amountMicros' ? (
|
||||||
<FormNumberFieldInput
|
<FormNumberFieldInput
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
) : filterType === 'PHONES' ? (
|
) : filterType === 'PHONES' ? (
|
||||||
@ -58,20 +56,20 @@ export const AdvancedFilterValueFormCompositeFieldInput = ({
|
|||||||
<FormNumberFieldInput
|
<FormNumberFieldInput
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<FormTextFieldInput
|
<FormTextFieldInput
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<FormTextFieldInput
|
<FormTextFieldInput
|
||||||
defaultValue={recordFilter.value}
|
defaultValue={recordFilter.value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
VariablePicker={VariablePicker}
|
VariablePicker={WorkflowVariablePicker}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
@ -1,8 +1,6 @@
|
|||||||
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
import { availableFieldMetadataItemsForFilterFamilySelector } from '@/object-metadata/states/availableFieldMetadataItemsForFilterFamilySelector';
|
||||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||||
import { AdvancedFilterAddFilterRuleSelect } from '@/object-record/advanced-filter/components/AdvancedFilterAddFilterRuleSelect';
|
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 { useChildRecordFiltersAndRecordFilterGroups } from '@/object-record/advanced-filter/hooks/useChildRecordFiltersAndRecordFilterGroups';
|
||||||
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
|
import { AdvancedFilterContext } from '@/object-record/advanced-filter/states/context/AdvancedFilterContext';
|
||||||
import { rootLevelRecordFilterGroupComponentSelector } from '@/object-record/advanced-filter/states/rootLevelRecordFilterGroupComponentSelector';
|
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 { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
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 { 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 { 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 styled from '@emotion/styled';
|
||||||
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
import { useRecoilCallback, useRecoilValue } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
@ -110,7 +109,7 @@ export const WorkflowFindRecordsFilters = ({
|
|||||||
<AdvancedFilterContext.Provider
|
<AdvancedFilterContext.Provider
|
||||||
value={{
|
value={{
|
||||||
onUpdate,
|
onUpdate,
|
||||||
isColumn: true,
|
isWorkflowFindRecords: true,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isDefined(rootRecordFilterGroup) ? (
|
{isDefined(rootRecordFilterGroup) ? (
|
||||||
@ -121,20 +120,18 @@ export const WorkflowFindRecordsFilters = ({
|
|||||||
isRecordFilterGroupChildARecordFilterGroup(
|
isRecordFilterGroupChildARecordFilterGroup(
|
||||||
recordFilterGroupChild,
|
recordFilterGroupChild,
|
||||||
) ? (
|
) ? (
|
||||||
<AdvancedFilterRecordFilterGroupColumn
|
<WorkflowAdvancedFilterRecordFilterGroupColumn
|
||||||
key={recordFilterGroupChild.id}
|
key={recordFilterGroupChild.id}
|
||||||
parentRecordFilterGroup={rootRecordFilterGroup}
|
parentRecordFilterGroup={rootRecordFilterGroup}
|
||||||
recordFilterGroup={recordFilterGroupChild}
|
recordFilterGroup={recordFilterGroupChild}
|
||||||
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
recordFilterGroupIndex={recordFilterGroupChildIndex}
|
||||||
VariablePicker={WorkflowVariablePicker}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<AdvancedFilterRecordFilterColumn
|
<WorkflowAdvancedFilterRecordFilterColumn
|
||||||
key={recordFilterGroupChild.id}
|
key={recordFilterGroupChild.id}
|
||||||
recordFilterGroup={rootRecordFilterGroup}
|
recordFilterGroup={rootRecordFilterGroup}
|
||||||
recordFilter={recordFilterGroupChild}
|
recordFilter={recordFilterGroupChild}
|
||||||
recordFilterIndex={recordFilterGroupChildIndex}
|
recordFilterIndex={recordFilterGroupChildIndex}
|
||||||
VariablePicker={WorkflowVariablePicker}
|
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
|
|||||||
import { WorkflowActionType } from '@/workflow/types/Workflow';
|
import { WorkflowActionType } from '@/workflow/types/Workflow';
|
||||||
import { FieldMetadataType } from '~/generated/graphql';
|
import { FieldMetadataType } from '~/generated/graphql';
|
||||||
|
|
||||||
const DISPLAYABLE_FIELD_TYPES_FOR_UPDATE = [
|
const COMMON_DISPLAYABLE_FIELD_TYPES = [
|
||||||
FieldMetadataType.TEXT,
|
FieldMetadataType.TEXT,
|
||||||
FieldMetadataType.NUMBER,
|
FieldMetadataType.NUMBER,
|
||||||
FieldMetadataType.DATE,
|
FieldMetadataType.DATE,
|
||||||
@ -20,6 +20,12 @@ const DISPLAYABLE_FIELD_TYPES_FOR_UPDATE = [
|
|||||||
FieldMetadataType.UUID,
|
FieldMetadataType.UUID,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const FIND_RECORDS_DISPLAYABLE_FIELD_TYPES = [
|
||||||
|
...COMMON_DISPLAYABLE_FIELD_TYPES,
|
||||||
|
FieldMetadataType.ARRAY,
|
||||||
|
FieldMetadataType.RELATION,
|
||||||
|
];
|
||||||
|
|
||||||
export const shouldDisplayFormField = ({
|
export const shouldDisplayFormField = ({
|
||||||
fieldMetadataItem,
|
fieldMetadataItem,
|
||||||
actionType,
|
actionType,
|
||||||
@ -37,9 +43,14 @@ export const shouldDisplayFormField = ({
|
|||||||
break;
|
break;
|
||||||
case 'UPDATE_RECORD':
|
case 'UPDATE_RECORD':
|
||||||
isTypeAllowedForAction =
|
isTypeAllowedForAction =
|
||||||
DISPLAYABLE_FIELD_TYPES_FOR_UPDATE.includes(fieldMetadataItem.type) ||
|
COMMON_DISPLAYABLE_FIELD_TYPES.includes(fieldMetadataItem.type) ||
|
||||||
fieldMetadataItem.settings?.['relationType'] === 'MANY_TO_ONE';
|
fieldMetadataItem.settings?.['relationType'] === 'MANY_TO_ONE';
|
||||||
break;
|
break;
|
||||||
|
case 'FIND_RECORDS':
|
||||||
|
isTypeAllowedForAction = FIND_RECORDS_DISPLAYABLE_FIELD_TYPES.includes(
|
||||||
|
fieldMetadataItem.type,
|
||||||
|
);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Action "${actionType}" is not supported`);
|
throw new Error(`Action "${actionType}" is not supported`);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,4 +26,5 @@ export { isValidUrl } from './url/isValidUrl';
|
|||||||
export { isDefined } from './validation/isDefined';
|
export { isDefined } from './validation/isDefined';
|
||||||
export { isValidLocale } from './validation/isValidLocale';
|
export { isValidLocale } from './validation/isValidLocale';
|
||||||
export { isValidUuid } from './validation/isValidUuid';
|
export { isValidUuid } from './validation/isValidUuid';
|
||||||
|
export { isValidVariable } from './validation/isValidVariable';
|
||||||
export { normalizeLocale } from './validation/normalizeLocale';
|
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