diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 047faeb17..fc3365440 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -732,6 +732,7 @@ export type FeatureFlagDto = { export enum FeatureFlagKey { IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED', + IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED', IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 1ffaa18b2..6d8bc0356 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -696,6 +696,7 @@ export type FeatureFlagDto = { export enum FeatureFlagKey { IS_AIRTABLE_INTEGRATION_ENABLED = 'IS_AIRTABLE_INTEGRATION_ENABLED', IS_AI_ENABLED = 'IS_AI_ENABLED', + IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED', IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', IS_IMAP_SMTP_CALDAV_ENABLED = 'IS_IMAP_SMTP_CALDAV_ENABLED', IS_JSON_FILTER_ENABLED = 'IS_JSON_FILTER_ENABLED', diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput.tsx new file mode 100644 index 000000000..a4d12c023 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput.tsx @@ -0,0 +1,27 @@ +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState'; +import { useLingui } from '@lingui/react/macro'; + +export const ObjectFilterDropdownAnyFieldSearchInput = () => { + const { t } = useLingui(); + + const [viewAnyFieldSearchValue, setViewAnyFieldSearchValue] = + useRecoilComponentStateV2(viewAnyFieldSearchValueComponentState); + + const handleSearchChange = (event: React.ChangeEvent) => { + const inputValue = event.target.value; + + setViewAnyFieldSearchValue(inputValue); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx index f53d4013e..ad9a40433 100644 --- a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/components/ObjectFilterDropdownTextInput.tsx @@ -42,7 +42,7 @@ export const ObjectFilterDropdownTextInput = () => { { componentInstanceId, ); + const objectFilterDropdownAnyFieldSearchIsSelectedCallbackState = + useRecoilComponentCallbackStateV2( + objectFilterDropdownAnyFieldSearchIsSelectedComponentState, + componentInstanceId, + ); + const objectFilterDropdownIsSelectingCompositeFieldCallbackState = useRecoilComponentCallbackStateV2( objectFilterDropdownIsSelectingCompositeFieldComponentState, @@ -53,6 +60,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { set(objectFilterDropdownIsSelectingCompositeFieldCallbackState, false); set(fieldMetadataItemIdUsedInDropdownCallbackState, null); set(objectFilterDropdownCurrentRecordFilterCallbackState, null); + set(objectFilterDropdownAnyFieldSearchIsSelectedCallbackState, false); }, [ objectFilterDropdownSearchInputCallbackState, @@ -61,6 +69,7 @@ export const useResetFilterDropdown = (componentInstanceId?: string) => { objectFilterDropdownIsSelectingCompositeFieldCallbackState, fieldMetadataItemIdUsedInDropdownCallbackState, objectFilterDropdownCurrentRecordFilterCallbackState, + objectFilterDropdownAnyFieldSearchIsSelectedCallbackState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState.ts b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState.ts new file mode 100644 index 000000000..1d7dbc36e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState.ts @@ -0,0 +1,9 @@ +import { ObjectFilterDropdownComponentInstanceContext } from '@/object-record/object-filter-dropdown/states/contexts/ObjectFilterDropdownComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const objectFilterDropdownAnyFieldSearchIsSelectedComponentState = + createComponentStateV2({ + key: 'objectFilterDropdownAnyFieldSearchIsSelectedComponentState', + defaultValue: false, + componentInstanceContext: ObjectFilterDropdownComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/views/components/AnyFieldSearchChip.tsx b/packages/twenty-front/src/modules/views/components/AnyFieldSearchChip.tsx new file mode 100644 index 000000000..c088d4bbb --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/AnyFieldSearchChip.tsx @@ -0,0 +1,29 @@ +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +import { SortOrFilterChip } from '@/views/components/SortOrFilterChip'; +import { ADVANCED_FILTER_DROPDOWN_ID } from '@/views/constants/AdvancedFilterDropdownId'; +import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState'; +import { IconFilter } from 'twenty-ui/display'; + +export const AnyFieldSearchChip = () => { + const { closeDropdown } = useCloseDropdown(); + + const [viewAnyFieldSearchValue, setViewAnyFieldSearchValue] = + useRecoilComponentStateV2(viewAnyFieldSearchValueComponentState); + + const handleRemoveClick = () => { + closeDropdown(); + setViewAnyFieldSearchValue(''); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownButton.tsx b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownButton.tsx new file mode 100644 index 000000000..4e4f450f5 --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownButton.tsx @@ -0,0 +1,28 @@ +import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; + +import { DROPDOWN_OFFSET_Y } from '@/ui/layout/dropdown/constants/DropdownOffsetY'; +import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; +import { AnyFieldSearchChip } from '@/views/components/AnyFieldSearchChip'; +import { AnyFieldSearchDropdownContent } from '@/views/components/AnyFieldSearchDropdownContent'; +import { ANY_FIELD_SEARCH_DROPDOWN_ID } from '@/views/constants/AnyFieldSearchDropdownId'; + +export const AnyFieldSearchDropdownButton = () => { + const { openDropdown } = useOpenDropdown(); + + const handleOpenAnyFieldSearchDropdown = () => { + openDropdown({ + dropdownComponentInstanceIdFromProps: ANY_FIELD_SEARCH_DROPDOWN_ID, + }); + }; + + return ( + } + dropdownComponents={} + dropdownOffset={{ y: DROPDOWN_OFFSET_Y, x: 0 }} + dropdownPlacement="bottom-start" + onOpen={handleOpenAnyFieldSearchDropdown} + /> + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContent.tsx b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContent.tsx new file mode 100644 index 000000000..75c86c502 --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContent.tsx @@ -0,0 +1,13 @@ +import { ObjectFilterDropdownAnyFieldSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { AnyFieldSearchDropdownContentMenuHeader } from '@/views/components/AnyFieldSearchDropdownContentMenuHeader'; + +export const AnyFieldSearchDropdownContent = () => { + return ( + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContentMenuHeader.tsx b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContentMenuHeader.tsx new file mode 100644 index 000000000..7ba7d7b2a --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/AnyFieldSearchDropdownContentMenuHeader.tsx @@ -0,0 +1,28 @@ +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown'; +import { useLingui } from '@lingui/react/macro'; +import { IconX } from 'twenty-ui/display'; + +export const AnyFieldSearchDropdownContentMenuHeader = () => { + const { t } = useLingui(); + + const { closeDropdown } = useCloseDropdown(); + + const handleBackButtonClick = () => { + closeDropdown(); + }; + + return ( + + } + > + {t`Search any field`} + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx b/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx index 20881a6d9..cb6c4d2c6 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx @@ -22,12 +22,16 @@ import { useAreViewFiltersDifferentFromRecordFilters } from '@/views/hooks/useAr import { useAreViewSortsDifferentFromRecordSorts } from '@/views/hooks/useAreViewSortsDifferentFromRecordSorts'; import { currentRecordFilterGroupsComponentState } from '@/object-record/record-filter-group/states/currentRecordFilterGroupsComponentState'; +import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import { AnyFieldSearchDropdownButton } from '@/views/components/AnyFieldSearchDropdownButton'; +import { ANY_FIELD_SEARCH_DROPDOWN_ID } from '@/views/constants/AnyFieldSearchDropdownId'; import { useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups } from '@/views/hooks/useApplyCurrentViewFilterGroupsToCurrentRecordFilterGroups'; import { useAreViewFilterGroupsDifferentFromRecordFilterGroups } from '@/views/hooks/useAreViewFilterGroupsDifferentFromRecordFilterGroups'; import { isViewBarExpandedComponentState } from '@/views/states/isViewBarExpandedComponentState'; +import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState'; import { t } from '@lingui/core/macro'; -import { isNonEmptyArray } from '@sniptt/guards'; +import { isNonEmptyArray, isNonEmptyString } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { LightButton } from 'twenty-ui/input'; @@ -119,6 +123,10 @@ export const ViewBarDetails = ({ currentRecordSortsComponentState, ); + const viewAnyFieldSearchValue = useRecoilComponentValueV2( + viewAnyFieldSearchValueComponentState, + ); + const { objectNameSingular } = useObjectNameSingularFromPlural({ objectNamePlural: objectNamePlural, }); @@ -172,7 +180,19 @@ export const ViewBarDetails = ({ toggleSoftDeleteFilterState(false); }; + const shouldShowAdvancedFilterDropdownButton = + currentRecordFilterGroups.length > 0; + + const isAnyFieldSearchDropdownOpen = useRecoilComponentValueV2( + isDropdownOpenComponentState, + ANY_FIELD_SEARCH_DROPDOWN_ID, + ); + + const shouldShowAnyFieldSearchChip = + isNonEmptyString(viewAnyFieldSearchValue) || isAnyFieldSearchDropdownOpen; + const shouldExpandViewBar = + shouldShowAnyFieldSearchChip || viewFiltersAreDifferentFromRecordFilters || viewSortsAreDifferentFromRecordSorts || viewFilterGroupsAreDifferentFromRecordFilterGroups || @@ -185,9 +205,6 @@ export const ViewBarDetails = ({ return null; } - const shouldShowAdvancedFilterDropdownButton = - currentRecordFilterGroups.length > 0; - return ( @@ -220,6 +237,7 @@ export const ViewBarDetails = ({ )} + {shouldShowAnyFieldSearchChip && } {shouldShowAdvancedFilterDropdownButton && ( )} diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButton.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButton.tsx new file mode 100644 index 000000000..1f1fb11ed --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButton.tsx @@ -0,0 +1,17 @@ +import { ViewBarFilterDropdownAnyFieldSearchButtonMenuItem } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem'; +import { useOpenAnyFieldSearchFilterFromViewBar } from '@/views/hooks/useOpenAnyFieldSearchFilterFromViewBar'; + +export const ViewBarFilterDropdownAnyFieldSearchButton = () => { + const { openAnyFieldSearchFilterFromViewBar } = + useOpenAnyFieldSearchFilterFromViewBar(); + + const handleSearchClick = () => { + openAnyFieldSearchFilterFromViewBar(); + }; + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx new file mode 100644 index 000000000..f47a2ebb4 --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchButtonMenuItem.tsx @@ -0,0 +1,57 @@ +import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem'; +import { isSelectedItemIdComponentFamilySelector } from '@/ui/layout/selectable-list/states/selectors/isSelectedItemIdComponentFamilySelector'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { IconSearch } from 'twenty-ui/display'; +import { MenuItem } from 'twenty-ui/navigation'; + +import { VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS } from '@/views/constants/ViewBarFilterBottomMenuItemIds'; + +import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; + +const StyledSearchText = styled.span` + color: ${({ theme }) => theme.font.color.light}; + margin-left: ${({ theme }) => theme.spacing(1)}; +`; + +type ViewBarFilterDropdownAnyFieldSearchButtonMenuItemProps = { + onClick?: () => void; +}; + +export const ViewBarFilterDropdownAnyFieldSearchButtonMenuItem = ({ + onClick, +}: ViewBarFilterDropdownAnyFieldSearchButtonMenuItemProps) => { + const { t } = useLingui(); + + const objectFilterDropdownSearchInput = useRecoilComponentValueV2( + objectFilterDropdownSearchInputComponentState, + ); + + const isSelected = useRecoilComponentFamilyValueV2( + isSelectedItemIdComponentFamilySelector, + VIEW_BAR_FILTER_BOTTOM_MENU_ITEM_IDS.SEARCH, + ); + + return ( + + + {t`Search any field`} + {objectFilterDropdownSearchInput && ( + {t`ยท ${objectFilterDropdownSearchInput}`} + )} + + } + /> + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInput.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInput.tsx new file mode 100644 index 000000000..e84de1806 --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInput.tsx @@ -0,0 +1,13 @@ +import { ObjectFilterDropdownAnyFieldSearchInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownAnyFieldSearchInput'; +import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; +import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth'; +import { ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader'; + +export const ViewBarFilterDropdownAnyFieldSearchInput = () => { + return ( + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader.tsx new file mode 100644 index 000000000..d3301bc75 --- /dev/null +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader.tsx @@ -0,0 +1,28 @@ +import { useResetFilterDropdown } from '@/object-record/object-filter-dropdown/hooks/useResetFilterDropdown'; +import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; +import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; +import { useLingui } from '@lingui/react/macro'; +import { IconChevronLeft } from 'twenty-ui/display'; + +export const ViewBarFilterDropdownAnyFieldSearchInputDropdownHeader = () => { + const { t } = useLingui(); + + const { resetFilterDropdown } = useResetFilterDropdown(); + + const handleBackButtonClick = () => { + resetFilterDropdown(); + }; + + return ( + + } + > + {t`Search any field`} + + ); +}; diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownBottomMenu.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownBottomMenu.tsx index d9ca5a3e4..f32f03b00 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownBottomMenu.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownBottomMenu.tsx @@ -1,6 +1,9 @@ import { ViewBarFilterDropdownAdvancedFilterButton } from '@/views/components/ViewBarFilterDropdownAdvancedFilterButton'; +import { ViewBarFilterDropdownAnyFieldSearchButton } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchButton'; import { ViewBarFilterDropdownVectorSearchButton } from '@/views/components/ViewBarFilterDropdownVectorSearchButton'; +import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import styled from '@emotion/styled'; +import { FeatureFlagKey } from '~/generated-metadata/graphql'; const StyledContainer = styled.div` display: flex; @@ -11,9 +14,17 @@ const StyledContainer = styled.div` `; export const ViewBarFilterDropdownBottomMenu = () => { + const isAnyFieldSearchEnabled = useIsFeatureEnabled( + FeatureFlagKey.IS_ANY_FIELD_SEARCH_ENABLED, + ); + return ( - + {isAnyFieldSearchEnabled ? ( + + ) : ( + + )} ); diff --git a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownContent.tsx b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownContent.tsx index a2a054d47..2345e9fbb 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownContent.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarFilterDropdownContent.tsx @@ -1,7 +1,9 @@ +import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState'; import { objectFilterDropdownFilterIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownFilterIsSelectedComponentState'; import { selectedOperandInDropdownComponentState } from '@/object-record/object-filter-dropdown/states/selectedOperandInDropdownComponentState'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { ViewBarFilterDropdownAnyFieldSearchInput } from '@/views/components/ViewBarFilterDropdownAnyFieldSearchInput'; import { ViewBarFilterDropdownFieldSelectMenu } from '@/views/components/ViewBarFilterDropdownFieldSelectMenu'; import { ViewBarFilterDropdownFilterInput } from '@/views/components/ViewBarFilterDropdownFilterInput'; import { ViewBarFilterDropdownVectorSearchInput } from '@/views/components/ViewBarFilterDropdownVectorSearchInput'; @@ -14,6 +16,11 @@ export const ViewBarFilterDropdownContent = () => { VIEW_BAR_FILTER_DROPDOWN_ID, ); + const objectFilterDropdownAnyFieldSearchIsSelected = + useRecoilComponentValueV2( + objectFilterDropdownAnyFieldSearchIsSelectedComponentState, + ); + const selectedOperandInDropdown = useRecoilComponentValueV2( selectedOperandInDropdownComponentState, ); @@ -21,6 +28,12 @@ export const ViewBarFilterDropdownContent = () => { const isVectorSearchFilter = selectedOperandInDropdown === ViewFilterOperand.VectorSearch; + const isAnyFieldSearchFilter = objectFilterDropdownAnyFieldSearchIsSelected; + + if (isAnyFieldSearchFilter) { + return ; + } + if (isVectorSearchFilter) { return ; } diff --git a/packages/twenty-front/src/modules/views/constants/AnyFieldSearchDropdownId.ts b/packages/twenty-front/src/modules/views/constants/AnyFieldSearchDropdownId.ts new file mode 100644 index 000000000..b95624709 --- /dev/null +++ b/packages/twenty-front/src/modules/views/constants/AnyFieldSearchDropdownId.ts @@ -0,0 +1 @@ +export const ANY_FIELD_SEARCH_DROPDOWN_ID = 'any-field-search-dropdown'; diff --git a/packages/twenty-front/src/modules/views/hooks/useOpenAnyFieldSearchFilterFromViewBar.ts b/packages/twenty-front/src/modules/views/hooks/useOpenAnyFieldSearchFilterFromViewBar.ts new file mode 100644 index 000000000..091c174fd --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/useOpenAnyFieldSearchFilterFromViewBar.ts @@ -0,0 +1,38 @@ +import { objectFilterDropdownAnyFieldSearchIsSelectedComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownAnyFieldSearchIsSelectedComponentState'; +import { objectFilterDropdownSearchInputComponentState } from '@/object-record/object-filter-dropdown/states/objectFilterDropdownSearchInputComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { viewAnyFieldSearchValueComponentState } from '@/views/states/viewAnyFieldSearchValueComponentState'; +import { isNonEmptyString } from '@sniptt/guards'; + +export const useOpenAnyFieldSearchFilterFromViewBar = () => { + const setViewAnyFieldSearchValueComponentState = useSetRecoilComponentStateV2( + viewAnyFieldSearchValueComponentState, + ); + + const setObjectFilterDropdownAnyFieldSearchIsSelectedComponentState = + useSetRecoilComponentStateV2( + objectFilterDropdownAnyFieldSearchIsSelectedComponentState, + ); + + const objectFilterDropdownSearchInput = useRecoilComponentValueV2( + objectFilterDropdownSearchInputComponentState, + ); + + const openAnyFieldSearchFilterFromViewBar = () => { + const userHasAlreadyEnteredSearchInputForObjectDropdownSearch = + isNonEmptyString(objectFilterDropdownSearchInput); + + if (userHasAlreadyEnteredSearchInputForObjectDropdownSearch) { + const filterValue = objectFilterDropdownSearchInput; + + setViewAnyFieldSearchValueComponentState(filterValue); + } + + setObjectFilterDropdownAnyFieldSearchIsSelectedComponentState(true); + }; + + return { + openAnyFieldSearchFilterFromViewBar, + }; +}; diff --git a/packages/twenty-front/src/modules/views/states/viewAnyFieldSearchValueComponentState.ts b/packages/twenty-front/src/modules/views/states/viewAnyFieldSearchValueComponentState.ts new file mode 100644 index 000000000..57a450ef1 --- /dev/null +++ b/packages/twenty-front/src/modules/views/states/viewAnyFieldSearchValueComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const viewAnyFieldSearchValueComponentState = + createComponentStateV2({ + key: 'viewAnyFieldSearchValueComponentState', + defaultValue: '', + componentInstanceContext: ViewComponentInstanceContext, + }); diff --git a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts index edf2da73a..5b1c4f160 100644 --- a/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts +++ b/packages/twenty-server/src/engine/core-modules/feature-flag/enums/feature-flag-key.enum.ts @@ -11,4 +11,5 @@ export enum FeatureFlagKey { IS_RELATION_CONNECT_ENABLED = 'IS_RELATION_CONNECT_ENABLED', IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED = 'IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED', IS_FIELDS_PERMISSIONS_ENABLED = 'IS_FIELDS_PERMISSIONS_ENABLED', + IS_ANY_FIELD_SEARCH_ENABLED = 'IS_ANY_FIELD_SEARCH_ENABLED', } diff --git a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts index 2f5b1836c..87752758f 100644 --- a/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts +++ b/packages/twenty-server/src/engine/twenty-orm/entity-manager/workspace-entity-manager.spec.ts @@ -107,6 +107,7 @@ describe('WorkspaceEntityManager', () => { IS_RELATION_CONNECT_ENABLED: false, IS_WORKSPACE_API_KEY_WEBHOOK_GRAPHQL_ENABLED: false, IS_FIELDS_PERMISSIONS_ENABLED: false, + IS_ANY_FIELD_SEARCH_ENABLED: false, }, eventEmitterService: { emitMutationEvent: jest.fn(), diff --git a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts index 0af48112b..2a1d61977 100644 --- a/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts +++ b/packages/twenty-server/src/engine/workspace-manager/dev-seeder/core/utils/seed-feature-flags.util.ts @@ -55,6 +55,11 @@ export const seedFeatureFlags = async ( workspaceId: workspaceId, value: true, }, + { + key: FeatureFlagKey.IS_ANY_FIELD_SEARCH_ENABLED, + workspaceId: workspaceId, + value: true, + }, ]) .execute(); };