Improve multi word filtering (#5034)
improve multi word search closes #4212 closes #3386
This commit is contained in:
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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 = () => {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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({
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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}%` } },
|
||||||
])
|
])
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { FieldMetadataType } from '~/generated-metadata/graphql.ts';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
formatFieldMetadataItemInput,
|
formatFieldMetadataItemInput,
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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`
|
||||||
|
|||||||
@ -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 = () => {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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'>,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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 {
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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`
|
||||||
|
|||||||
@ -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';
|
||||||
|
|||||||
@ -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';
|
||||||
|
|
||||||
|
|||||||
@ -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%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -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}%`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user