Improve multi word filtering (#5034)

improve multi word search

closes #4212 
closes #3386
This commit is contained in:
martmull
2024-04-18 15:46:59 +02:00
committed by GitHub
parent 88c14b7e52
commit 1c1a055c94
54 changed files with 212 additions and 146 deletions

View File

@ -2,7 +2,7 @@ import { Route, Routes, useLocation } from 'react-router-dom';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { VerifyEffect } from '@/auth/components/VerifyEffect'; import { VerifyEffect } from '@/auth/components/VerifyEffect';
import { billingState } from '@/client-config/states/billingState.ts'; import { billingState } from '@/client-config/states/billingState';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';
import { BlankLayout } from '@/ui/layout/page/BlankLayout'; import { BlankLayout } from '@/ui/layout/page/BlankLayout';

View File

@ -11,7 +11,7 @@ import { iconsState } from 'twenty-ui';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState.ts'; import { isCurrentUserLoadedState } from '@/auth/states/isCurrentUserLoadingState';
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState'; import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
import { workspacesState } from '@/auth/states/workspaces'; import { workspacesState } from '@/auth/states/workspaces';
import { authProvidersState } from '@/client-config/states/authProvidersState'; import { authProvidersState } from '@/client-config/states/authProvidersState';

View File

@ -6,11 +6,11 @@ import { motion } from 'framer-motion';
import { useRecoilState } from 'recoil'; import { useRecoilState } from 'recoil';
import { IconGoogle } from 'twenty-ui'; import { IconGoogle } from 'twenty-ui';
import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword.ts'; import { useHandleResetPassword } from '@/auth/sign-in-up/hooks/useHandleResetPassword';
import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm.ts'; import { useSignInUpForm } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle.ts'; import { useSignInWithGoogle } from '@/auth/sign-in-up/hooks/useSignInWithGoogle';
import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash.ts'; import { useWorkspaceFromInviteHash } from '@/auth/sign-in-up/hooks/useWorkspaceFromInviteHash';
import { authProvidersState } from '@/client-config/states/authProvidersState.ts'; import { authProvidersState } from '@/client-config/states/authProvidersState';
import { Loader } from '@/ui/feedback/loader/components/Loader'; import { Loader } from '@/ui/feedback/loader/components/Loader';
import { MainButton } from '@/ui/input/button/components/MainButton'; import { MainButton } from '@/ui/input/button/components/MainButton';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';

View File

@ -2,10 +2,10 @@ import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState.ts'; import { CurrentWorkspace } from '@/auth/states/currentWorkspaceState';
import { previousUrlState } from '@/auth/states/previousUrlState'; import { previousUrlState } from '@/auth/states/previousUrlState';
import { billingState } from '@/client-config/states/billingState.ts'; import { billingState } from '@/client-config/states/billingState';
import { AppPath } from '@/types/AppPath.ts'; import { AppPath } from '@/types/AppPath';
import { WorkspaceMember } from '~/generated/graphql.tsx'; import { WorkspaceMember } from '~/generated/graphql.tsx';
export const useNavigateAfterSignInUp = () => { export const useNavigateAfterSignInUp = () => {

View File

@ -2,8 +2,8 @@ import { useCallback, useState } from 'react';
import { SubmitHandler, UseFormReturn } from 'react-hook-form'; import { SubmitHandler, UseFormReturn } from 'react-hook-form';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp.ts'; import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp';
import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm.ts'; import { Form } from '@/auth/sign-in-up/hooks/useSignInUpForm';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation'; import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';

View File

@ -4,8 +4,8 @@ import { zodResolver } from '@hookform/resolvers/zod';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { z } from 'zod'; import { z } from 'zod';
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex.ts'; import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState.ts'; import { isSignInPrefilledState } from '@/client-config/states/isSignInPrefilledState';
const validationSchema = z const validationSchema = z
.object({ .object({

View File

@ -1,6 +1,6 @@
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { useAuth } from '@/auth/hooks/useAuth.ts'; import { useAuth } from '@/auth/hooks/useAuth';
export const useSignInWithGoogle = () => { export const useSignInWithGoogle = () => {
const workspaceInviteHash = useParams().workspaceInviteHash; const workspaceInviteHash = useParams().workspaceInviteHash;

View File

@ -1,7 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { SubscriptionCardPrice } from '@/billing/components/SubscriptionCardPrice.tsx'; import { SubscriptionCardPrice } from '@/billing/components/SubscriptionCardPrice.tsx';
import { capitalize } from '~/utils/string/capitalize.ts'; import { capitalize } from '~/utils/string/capitalize';
type SubscriptionCardProps = { type SubscriptionCardProps = {
type?: string; type?: string;

View File

@ -22,6 +22,7 @@ import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useLis
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile'; import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import { getLogoUrlFromDomainName } from '~/utils'; import { getLogoUrlFromDomainName } from '~/utils';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
import { useCommandMenu } from '../hooks/useCommandMenu'; import { useCommandMenu } from '../hooks/useCommandMenu';
@ -142,8 +143,10 @@ export const CommandMenu = () => {
objectNameSingular: CoreObjectNameSingular.Person, objectNameSingular: CoreObjectNameSingular.Person,
filter: commandMenuSearch filter: commandMenuSearch
? makeOrFilterVariables([ ? makeOrFilterVariables([
{ name: { firstName: { ilike: `%${commandMenuSearch}%` } } }, ...generateILikeFiltersForCompositeFields(commandMenuSearch, 'name', [
{ name: { lastName: { ilike: `%${commandMenuSearch}%` } } }, 'firstName',
'lastName',
]),
{ email: { ilike: `%${commandMenuSearch}%` } }, { email: { ilike: `%${commandMenuSearch}%` } },
{ phone: { ilike: `%${commandMenuSearch}%` } }, { phone: { ilike: `%${commandMenuSearch}%` } },
]) ])

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState.ts'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect'; import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope';

View File

@ -1,6 +1,6 @@
import { v4 } from 'uuid'; import { v4 } from 'uuid';
import { FieldMetadataOption } from '@/object-metadata/types/FieldMetadataOption.ts'; import { FieldMetadataOption } from '@/object-metadata/types/FieldMetadataOption';
import { getDefaultValueForBackend } from '@/object-metadata/utils/getDefaultValueForBackend'; import { getDefaultValueForBackend } from '@/object-metadata/utils/getDefaultValueForBackend';
import { Field } from '~/generated/graphql'; import { Field } from '~/generated/graphql';

View File

@ -1,6 +1,6 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError'; import { ObjectMetadataItemNotFoundError } from '@/object-metadata/errors/ObjectMetadataNotFoundError';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector'; import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';

View File

@ -1,6 +1,6 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector'; import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';

View File

@ -1,6 +1,6 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector'; import { objectMetadataItemFamilySelector } from '@/object-metadata/states/objectMetadataItemFamilySelector';
import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock'; import { getObjectMetadataItemsMock } from '@/object-metadata/utils/getObjectMetadataItemsMock';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { import {
formatFieldMetadataItemInput, formatFieldMetadataItemInput,

View File

@ -2,7 +2,7 @@ import toCamelCase from 'lodash.camelcase';
import toSnakeCase from 'lodash.snakecase'; import toSnakeCase from 'lodash.snakecase';
import { Field, FieldMetadataType } from '~/generated-metadata/graphql'; import { Field, FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined.ts'; import { isDefined } from '~/utils/isDefined';
import { FieldMetadataOption } from '../types/FieldMetadataOption'; import { FieldMetadataOption } from '../types/FieldMetadataOption';

View File

@ -24,7 +24,7 @@ import { isFieldDateTime } from '../types/guards/isFieldDateTime';
import { isFieldEmail } from '../types/guards/isFieldEmail'; import { isFieldEmail } from '../types/guards/isFieldEmail';
import { isFieldFullName } from '../types/guards/isFieldFullName'; import { isFieldFullName } from '../types/guards/isFieldFullName';
import { isFieldLink } from '../types/guards/isFieldLink'; import { isFieldLink } from '../types/guards/isFieldLink';
import { isFieldMultiSelect } from '../types/guards/isFieldMultiSelect.ts'; import { isFieldMultiSelect } from '../types/guards/isFieldMultiSelect';
import { isFieldNumber } from '../types/guards/isFieldNumber'; import { isFieldNumber } from '../types/guards/isFieldNumber';
import { isFieldPhone } from '../types/guards/isFieldPhone'; import { isFieldPhone } from '../types/guards/isFieldPhone';
import { isFieldRawJson } from '../types/guards/isFieldRawJson'; import { isFieldRawJson } from '../types/guards/isFieldRawJson';

View File

@ -9,7 +9,7 @@ import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope'; import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate'; import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDate';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts'; import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';

View File

@ -7,8 +7,8 @@ import { isFieldDate } from '@/object-record/record-field/types/guards/isFieldDa
import { isFieldDateValue } from '@/object-record/record-field/types/guards/isFieldDateValue'; import { isFieldDateValue } from '@/object-record/record-field/types/guards/isFieldDateValue';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue'; import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts'; import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts'; import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue'; import { isFieldRawJsonValue } from '@/object-record/record-field/types/guards/isFieldRawJsonValue';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';

View File

@ -1,6 +1,6 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField.ts'; import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
import { Tag } from '@/ui/display/tag/components/Tag'; import { Tag } from '@/ui/display/tag/components/Tag';
const StyledTagContainer = styled.div` const StyledTagContainer = styled.div`

View File

@ -1,14 +1,14 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext.ts'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { usePersistField } from '@/object-record/record-field/hooks/usePersistField.ts'; import { usePersistField } from '@/object-record/record-field/hooks/usePersistField';
import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput.ts'; import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput';
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata.ts'; import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata.ts'; import { assertFieldMetadata } from '@/object-record/record-field/types/guards/assertFieldMetadata';
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts'; import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts'; import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector.ts'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { FieldMetadataType } from '~/generated/graphql.tsx'; import { FieldMetadataType } from '~/generated/graphql.tsx';
export const useMultiSelectField = () => { export const useMultiSelectField = () => {

View File

@ -1,7 +1,7 @@
import { useRef, useState } from 'react'; import { useRef, useState } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField.ts'; import { useMultiSelectField } from '@/object-record/record-field/meta-types/hooks/useMultiSelectField';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldAddressMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldAddressMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldBooleanMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldBooleanMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldCurrencyMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldCurrencyMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldDateTimeMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldDateTimeMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldEmailMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldEmailMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldFullNameMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldFullNameMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldLinkMetadata, FieldMetadata } from '../FieldMetadata'; import { FieldLinkMetadata, FieldMetadata } from '../FieldMetadata';

View File

@ -1,9 +1,9 @@
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition.ts'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { import {
FieldMetadata, FieldMetadata,
FieldMultiSelectMetadata, FieldMultiSelectMetadata,
} from '@/object-record/record-field/types/FieldMetadata.ts'; } from '@/object-record/record-field/types/FieldMetadata';
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
export const isFieldMultiSelect = ( export const isFieldMultiSelect = (
field: Pick<FieldDefinition<FieldMetadata>, 'type'>, field: Pick<FieldDefinition<FieldMetadata>, 'type'>,

View File

@ -1,5 +1,5 @@
import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata.ts'; import { FieldMultiSelectValue } from '@/object-record/record-field/types/FieldMetadata';
import { multiSelectFieldValueSchema } from '@/object-record/record-field/validation-schemas/multiSelectFieldValueSchema.ts'; import { multiSelectFieldValueSchema } from '@/object-record/record-field/validation-schemas/multiSelectFieldValueSchema';
export const isFieldMultiSelectValue = ( export const isFieldMultiSelectValue = (
fieldValue: unknown, fieldValue: unknown,

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldNumberMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldNumberMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldPhoneMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldPhoneMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldRawJsonMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldRawJsonMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldRelationMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldRelationMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldTextMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldTextMetadata } from '../FieldMetadata';

View File

@ -1,4 +1,4 @@
import { FieldMetadataType } from '~/generated-metadata/graphql.ts'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldDefinition } from '../FieldDefinition'; import { FieldDefinition } from '../FieldDefinition';
import { FieldMetadata, FieldUuidMetadata } from '../FieldMetadata'; import { FieldMetadata, FieldUuidMetadata } from '../FieldMetadata';

View File

@ -12,8 +12,8 @@ import { isFieldFullName } from '@/object-record/record-field/types/guards/isFie
import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue'; import { isFieldFullNameValue } from '@/object-record/record-field/types/guards/isFieldFullNameValue';
import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink'; import { isFieldLink } from '@/object-record/record-field/types/guards/isFieldLink';
import { isFieldLinkValue } from '@/object-record/record-field/types/guards/isFieldLinkValue'; import { isFieldLinkValue } from '@/object-record/record-field/types/guards/isFieldLinkValue';
import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect.ts'; import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/isFieldMultiSelect';
import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue.ts'; import { isFieldMultiSelectValue } from '@/object-record/record-field/types/guards/isFieldMultiSelectValue';
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber'; import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';

View File

@ -5,7 +5,6 @@ import {
CurrencyFilter, CurrencyFilter,
DateFilter, DateFilter,
FloatFilter, FloatFilter,
FullNameFilter,
ObjectRecordQueryFilter, ObjectRecordQueryFilter,
StringFilter, StringFilter,
URLFilter, URLFilter,
@ -14,6 +13,7 @@ import {
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables'; import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { Field } from '~/generated/graphql'; import { Field } from '~/generated/graphql';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
import { Filter } from '../../object-filter-dropdown/types/Filter'; import { Filter } from '../../object-filter-dropdown/types/Filter';
@ -203,50 +203,25 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
); );
} }
break; break;
case 'FULL_NAME': case 'FULL_NAME': {
const fullNameFilters = generateILikeFiltersForCompositeFields(
rawUIFilter.value,
correspondingField.name,
['firstName', 'lastName'],
);
switch (rawUIFilter.operand) { switch (rawUIFilter.operand) {
case ViewFilterOperand.Contains: case ViewFilterOperand.Contains:
objectRecordFilters.push({ objectRecordFilters.push({
or: [ or: fullNameFilters,
{
[correspondingField.name]: {
firstName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
{
[correspondingField.name]: {
lastName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
],
}); });
break; break;
case ViewFilterOperand.DoesNotContain: case ViewFilterOperand.DoesNotContain:
objectRecordFilters.push({ objectRecordFilters.push({
and: [ and: fullNameFilters.map((filter) => {
{ return {
not: { not: filter,
[correspondingField.name]: { };
firstName: { }),
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
},
{
not: {
[correspondingField.name]: {
lastName: {
ilike: `%${rawUIFilter.value}%`,
},
} as FullNameFilter,
},
},
],
}); });
break; break;
default: default:
@ -255,6 +230,7 @@ export const turnObjectDropdownFilterIntoQueryFilter = (
); );
} }
break; break;
}
case 'ADDRESS': case 'ADDRESS':
switch (rawUIFilter.operand) { switch (rawUIFilter.operand) {
case ViewFilterOperand.Contains: case ViewFilterOperand.Contains:

View File

@ -1,6 +1,6 @@
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy';

View File

@ -9,7 +9,7 @@ import {
} from 'twenty-ui'; } from 'twenty-ui';
import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId'; import { RECORD_INDEX_OPTIONS_DROPDOWN_ID } from '@/object-record/record-index/options/constants/RecordIndexOptionsDropdownId';
import { useExportTableData } from '@/object-record/record-index/options/hooks/useExportTableData.ts'; import { useExportTableData } from '@/object-record/record-index/options/hooks/useExportTableData';
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard'; import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable'; import { useRecordIndexOptionsForTable } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForTable';
import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope'; import { TableOptionsHotkeyScope } from '@/object-record/record-table/types/TableOptionsHotkeyScope';

View File

@ -14,7 +14,7 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { RecordChip } from '@/object-record/components/RecordChip'; import { RecordChip } from '@/object-record/components/RecordChip';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord.ts'; import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { useLazyFindOneRecord } from '@/object-record/hooks/useLazyFindOneRecord'; import { useLazyFindOneRecord } from '@/object-record/hooks/useLazyFindOneRecord';
import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { import {

View File

@ -5,6 +5,7 @@ import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/get
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter'; import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables'; import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
export const useSearchFilterPerMetadataItem = ({ export const useSearchFilterPerMetadataItem = ({
@ -29,25 +30,16 @@ export const useSearchFilterPerMetadataItem = ({
switch (labelIdentifierFieldMetadataItem.type) { switch (labelIdentifierFieldMetadataItem.type) {
case FieldMetadataType.FullName: { case FieldMetadataType.FullName: {
if (isNonEmptyString(searchFilterValue)) { if (isNonEmptyString(searchFilterValue)) {
const fullNameFilter = makeOrFilterVariables([ const compositeFilter = makeOrFilterVariables(
{ generateILikeFiltersForCompositeFields(
[labelIdentifierFieldMetadataItem.name]: { searchFilterValue,
firstName: { labelIdentifierFieldMetadataItem.name,
ilike: `%${searchFilterValue}%`, ['firstName', 'lastName'],
}, ),
}, );
},
{
[labelIdentifierFieldMetadataItem.name]: {
lastName: {
ilike: `%${searchFilterValue}%`,
},
},
},
]);
if (isDefined(fullNameFilter)) { if (isDefined(compositeFilter)) {
searchFilter = fullNameFilter; searchFilter = compositeFilter;
} }
} }
break; break;

View File

@ -4,11 +4,13 @@ import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapTo
import { OrderBy } from '@/object-metadata/types/OrderBy'; import { OrderBy } from '@/object-metadata/types/OrderBy';
import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit'; import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect'; import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect';
import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables'; import { makeAndFilterVariables } from '@/object-record/utils/makeAndFilterVariables';
import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables'; import { makeOrFilterVariables } from '@/object-record/utils/makeOrFilterVariables';
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
type SearchFilter = { fieldNames: string[]; filter: string | number }; type SearchFilter = { fieldNames: string[]; filter: string | number };
@ -56,28 +58,33 @@ export const useFilteredSearchEntityQuery = ({
return undefined; return undefined;
} }
return makeOrFilterVariables( const formattedFilters = fieldNames.reduce(
fieldNames.map((fieldName) => { (previousValue: ObjectRecordQueryFilter[], fieldName) => {
const [parentFieldName, subFieldName] = fieldName.split('.'); const [parentFieldName, subFieldName] = fieldName.split('.');
if (isNonEmptyString(subFieldName)) { if (isNonEmptyString(subFieldName)) {
// Composite field // Composite field
return { return [
[parentFieldName]: { ...previousValue,
[subFieldName]: { ...generateILikeFiltersForCompositeFields(filter, parentFieldName, [
ilike: `%${filter}%`, subFieldName,
}, ]),
}, ];
};
} }
return { return [
[fieldName]: { ...previousValue,
ilike: `%${filter}%`, {
[fieldName]: {
ilike: `%${filter}%`,
},
}, },
}; ];
}), },
[],
); );
return makeOrFilterVariables(formattedFilters);
}); });
const { const {

View File

@ -17,7 +17,7 @@ import {
} from 'twenty-ui'; } from 'twenty-ui';
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { billingState } from '@/client-config/states/billingState.ts'; import { billingState } from '@/client-config/states/billingState';
import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem'; import { SettingsNavigationDrawerItem } from '@/settings/components/SettingsNavigationDrawerItem';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { SettingsPath } from '@/types/SettingsPath'; import { SettingsPath } from '@/types/SettingsPath';

View File

@ -1,4 +1,4 @@
import { NEVER_EXPIRE_DELTA_IN_YEARS } from '@/settings/developers/constants/NeverExpireDeltaInYears.ts'; import { NEVER_EXPIRE_DELTA_IN_YEARS } from '@/settings/developers/constants/NeverExpireDeltaInYears';
export const EXPIRATION_DATES: { export const EXPIRATION_DATES: {
value: number | null; value: number | null;

View File

@ -1,7 +1,7 @@
import { isNonEmptyString } from '@sniptt/guards'; import { isNonEmptyString } from '@sniptt/guards';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import { NEVER_EXPIRE_DELTA_IN_YEARS } from '@/settings/developers/constants/NeverExpireDeltaInYears.ts'; import { NEVER_EXPIRE_DELTA_IN_YEARS } from '@/settings/developers/constants/NeverExpireDeltaInYears';
import { ApiFieldItem } from '@/settings/developers/types/api-key/ApiFieldItem'; import { ApiFieldItem } from '@/settings/developers/types/api-key/ApiFieldItem';
import { ApiKey } from '@/settings/developers/types/api-key/ApiKey'; import { ApiKey } from '@/settings/developers/types/api-key/ApiKey';
import { beautifyDateDiff } from '~/utils/date-utils'; import { beautifyDateDiff } from '~/utils/date-utils';

View File

@ -7,10 +7,10 @@ import { SubTitle } from '@/auth/components/SubTitle.tsx';
import { Title } from '@/auth/components/Title.tsx'; import { Title } from '@/auth/components/Title.tsx';
import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit.tsx'; import { SubscriptionBenefit } from '@/billing/components/SubscriptionBenefit.tsx';
import { SubscriptionCard } from '@/billing/components/SubscriptionCard.tsx'; import { SubscriptionCard } from '@/billing/components/SubscriptionCard.tsx';
import { billingState } from '@/client-config/states/billingState.ts'; import { billingState } from '@/client-config/states/billingState';
import { AppPath } from '@/types/AppPath.ts'; import { AppPath } from '@/types/AppPath';
import { Loader } from '@/ui/feedback/loader/components/Loader.tsx'; import { Loader } from '@/ui/feedback/loader/components/Loader.tsx';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar.ts'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { MainButton } from '@/ui/input/button/components/MainButton.tsx'; import { MainButton } from '@/ui/input/button/components/MainButton.tsx';
import { CardPicker } from '@/ui/input/components/CardPicker.tsx'; import { CardPicker } from '@/ui/input/components/CardPicker.tsx';
import { import {

View File

@ -13,7 +13,7 @@ import { Logo } from '@/auth/components/Logo';
import { Title } from '@/auth/components/Title'; import { Title } from '@/auth/components/Title';
import { useAuth } from '@/auth/hooks/useAuth'; import { useAuth } from '@/auth/hooks/useAuth';
import { useIsLogged } from '@/auth/hooks/useIsLogged'; import { useIsLogged } from '@/auth/hooks/useIsLogged';
import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp.ts'; import { useNavigateAfterSignInUp } from '@/auth/sign-in-up/hooks/useNavigateAfterSignInUp';
import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex'; import { PASSWORD_REGEX } from '@/auth/utils/passwordRegex';
import { AppPath } from '@/types/AppPath'; import { AppPath } from '@/types/AppPath';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';

View File

@ -6,9 +6,9 @@ import { IconCheck } from 'twenty-ui';
import { SubTitle } from '@/auth/components/SubTitle.tsx'; import { SubTitle } from '@/auth/components/SubTitle.tsx';
import { Title } from '@/auth/components/Title.tsx'; import { Title } from '@/auth/components/Title.tsx';
import { AppPath } from '@/types/AppPath.ts'; import { AppPath } from '@/types/AppPath';
import { MainButton } from '@/ui/input/button/components/MainButton.tsx'; import { MainButton } from '@/ui/input/button/components/MainButton.tsx';
import { RGBA } from '@/ui/theme/constants/Rgba.ts'; import { RGBA } from '@/ui/theme/constants/Rgba';
import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn.tsx'; import { AnimatedEaseIn } from '@/ui/utilities/animation/components/AnimatedEaseIn.tsx';
const StyledCheckContainer = styled.div` const StyledCheckContainer = styled.div`

View File

@ -9,13 +9,13 @@ import {
IconCurrencyDollar, IconCurrencyDollar,
} from 'twenty-ui'; } from 'twenty-ui';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus.ts'; import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState.ts'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus.ts'; import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage'; import { SettingsBillingCoverImage } from '@/billing/components/SettingsBillingCoverImage';
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
import { SupportChat } from '@/support/components/SupportChat'; import { SupportChat } from '@/support/components/SupportChat';
import { AppPath } from '@/types/AppPath.ts'; import { AppPath } from '@/types/AppPath';
import { Info } from '@/ui/display/info/components/Info'; import { Info } from '@/ui/display/info/components/Info';
import { H1Title } from '@/ui/display/typography/components/H1Title'; import { H1Title } from '@/ui/display/typography/components/H1Title';
import { H2Title } from '@/ui/display/typography/components/H2Title'; import { H2Title } from '@/ui/display/typography/components/H2Title';

View File

@ -28,7 +28,7 @@ import { Section } from '@/ui/layout/section/components/Section';
import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb';
import { View } from '@/views/types/View'; import { View } from '@/views/types/View';
import { ViewType } from '@/views/types/ViewType'; import { ViewType } from '@/views/types/ViewType';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled.ts'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';

View File

@ -0,0 +1,64 @@
import { generateILikeFiltersForCompositeFields } from '~/utils/array/generateILikeFiltersForCompositeFields';
describe('generateILikeFiltersForCompositeFields', () => {
it('should format composite filters for simple filter string', () => {
expect(
generateILikeFiltersForCompositeFields('john', 'baseField', [
'subField1',
'subField2',
]),
).toEqual([
{
baseField: {
subField1: {
ilike: '%john%',
},
},
},
{
baseField: {
subField2: {
ilike: '%john%',
},
},
},
]);
});
it('should format composite filters for complex filter string', () => {
expect(
generateILikeFiltersForCompositeFields('john doe', 'name', [
'firstName',
'lastName',
]),
).toEqual([
{
name: {
firstName: {
ilike: '%john%',
},
},
},
{
name: {
lastName: {
ilike: '%john%',
},
},
},
{
name: {
firstName: {
ilike: '%doe%',
},
},
},
{
name: {
lastName: {
ilike: '%doe%',
},
},
},
]);
});
});

View File

@ -0,0 +1,24 @@
import { ObjectRecordQueryFilter } from '@/object-record/record-filter/types/ObjectRecordQueryFilter';
export const generateILikeFiltersForCompositeFields = (
filterString: string,
baseFieldName: string,
subFields: string[],
) => {
return filterString
.split(' ')
.reduce((previousValue: ObjectRecordQueryFilter[], currentValue) => {
return [
...previousValue,
...subFields.map((subField) => {
return {
[baseFieldName]: {
[subField]: {
ilike: `%${currentValue}%`,
},
},
};
}),
];
}, []);
};