diff --git a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx index 7719f4576..f85dc54ad 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx +++ b/packages/twenty-front/src/modules/activities/inline-cell/components/ActivityTargetInlineCellEditMode.tsx @@ -29,7 +29,7 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco import { ActivityTargetInlineCellEditModeMultiRecordsEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect'; import { ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect } from '@/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { prefillRecord } from '@/object-record/utils/prefillRecord'; const StyledSelectContainer = styled.div` @@ -52,7 +52,7 @@ export const ActivityTargetInlineCellEditMode = ({ activityObjectNameSingular, }: ActivityTargetInlineCellEditModeProps) => { const [isActivityInCreateMode] = useRecoilState(isActivityInCreateModeState); - const relationPickerScopeId = `relation-picker-${activity.id}`; + const recordPickerInstanceId = `record-picker-${activity.id}`; const selectedTargetObjectIds = activityTargetWithTargetRecords.map( (activityTarget) => ({ @@ -101,7 +101,7 @@ export const ActivityTargetInlineCellEditMode = ({ const record = snapshot .getLoadable( objectRecordMultiSelectComponentFamilyState({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, familyKey: activityTarget.targetObject.id, }), ) @@ -124,7 +124,7 @@ export const ActivityTargetInlineCellEditMode = ({ [ activityTargetWithTargetRecords, closeEditableField, - relationPickerScopeId, + recordPickerInstanceId, setActivityFromStore, ], ); @@ -142,7 +142,7 @@ export const ActivityTargetInlineCellEditMode = ({ const previouslyCheckedRecordsIds = snapshot .getLoadable( objectRecordMultiSelectCheckedRecordsIdsComponentState({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, }), ) .getValue(); @@ -153,7 +153,7 @@ export const ActivityTargetInlineCellEditMode = ({ const record = snapshot .getLoadable( objectRecordMultiSelectComponentFamilyState({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, familyKey: recordId, }), ) @@ -167,7 +167,7 @@ export const ActivityTargetInlineCellEditMode = ({ set( objectRecordMultiSelectCheckedRecordsIdsComponentState({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, }), (prev) => [...prev, recordId], ); @@ -237,7 +237,7 @@ export const ActivityTargetInlineCellEditMode = ({ set( objectRecordMultiSelectCheckedRecordsIdsComponentState({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, }), previouslyCheckedRecordsIds.filter((id) => id !== recordId), ); @@ -273,7 +273,7 @@ export const ActivityTargetInlineCellEditMode = ({ deleteManyActivityTargets, isActivityInCreateMode, objectMetadataItemActivityTarget, - relationPickerScopeId, + recordPickerInstanceId, upsertActivity, activityObjectNameSingular, ], @@ -281,7 +281,9 @@ export const ActivityTargetInlineCellEditMode = ({ return ( - + @@ -290,7 +292,7 @@ export const ActivityTargetInlineCellEditMode = ({ /> - + ); }; diff --git a/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts b/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts index 50dcf4d32..c4814a0d7 100644 --- a/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts +++ b/packages/twenty-front/src/modules/favorites/utils/sortFavorites.ts @@ -52,14 +52,14 @@ export const sortFavorites = ( if (isDefined(favorite[relationField.name])) { const relationObject = favorite[relationField.name]; - const relationObjectNameSingular = + const objectNameSingular = relationField.relationDefinition?.targetObjectMetadata .nameSingular ?? ''; const objectRecordIdentifier = getObjectRecordIdentifierByNameSingular( relationObject, - relationObjectNameSingular, + objectNameSingular, ); return { @@ -75,7 +75,7 @@ export const sortFavorites = ( : '', workspaceMemberId: favorite.workspaceMemberId, favoriteFolderId: favorite.favoriteFolderId, - objectNameSingular: relationObjectNameSingular, + objectNameSingular: objectNameSingular, } as ProcessedFavorite; } } diff --git a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsProvider.tsx b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsProvider.tsx index 7f18bb2c2..0a3f21e1e 100644 --- a/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsProvider.tsx +++ b/packages/twenty-front/src/modules/object-metadata/components/ObjectMetadataItemsProvider.tsx @@ -4,7 +4,7 @@ import { useRecoilValue } from 'recoil'; import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/ObjectMetadataItemsLoadEffect'; import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader'; export const ObjectMetadataItemsProvider = ({ @@ -19,9 +19,11 @@ export const ObjectMetadataItemsProvider = ({ {shouldDisplayChildren ? ( - + {children} - + ) : ( diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx index d8215faeb..34c0c3bed 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunity.tsx @@ -3,7 +3,7 @@ import styled from '@emotion/styled'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; -import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; +import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect'; import { useRecoilValue } from 'recoil'; const StyledCompanyPickerContainer = styled.div` @@ -37,15 +37,15 @@ export const RecordBoardColumnNewOpportunity = ({ <> {newRecord.isCreating && newRecord.position === position && ( - handleCreateSuccess(position, columnId, false)} - onEntitySelected={(company) => + onRecordSelected={(company) => company ? handleEntitySelect(position, company) : null } - relationObjectNameSingular={CoreObjectNameSingular.Company} - relationPickerScopeId="relation-picker" - selectedRelationRecordIds={[]} + objectNameSingular={CoreObjectNameSingular.Company} + recordPickerInstanceId="relation-picker" + selectedRecordIds={[]} /> )} diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts index 533a834e4..7fce3b598 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts @@ -1,8 +1,8 @@ import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { recordBoardNewRecordByColumnIdSelector } from '@/object-record/record-board/states/selectors/recordBoardNewRecordByColumnIdSelector'; -import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useCallback, useContext } from 'react'; @@ -20,8 +20,8 @@ export const useAddNewCard = () => { const columnContext = useContext(RecordBoardColumnContext); const { createOneRecord, selectFieldMetadataItem, objectMetadataItem } = useContext(RecordBoardContext); - const { resetSearchFilter } = useEntitySelectSearch({ - relationPickerScopeId: 'relation-picker', + const { resetSearchFilter } = useRecordSelectSearch({ + recordPickerInstanceId: 'record-picker', }); const { @@ -71,7 +71,7 @@ export const useAddNewCard = () => { labelValue: string, position: 'first' | 'last', isOpportunity: boolean, - company?: EntityForSelect, + company?: RecordForSelect, ) => { if ( (isOpportunity && company !== null) || @@ -220,7 +220,7 @@ export const useAddNewCard = () => { const handleEntitySelect = useCallback( ( position: 'first' | 'last', - company: EntityForSelect, + company: RecordForSelect, columnId?: string, ) => { const columnDefinitionId = getColumnDefinitionId(columnId); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardNewRecordByColumnIdComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardNewRecordByColumnIdComponentFamilyState.ts index e3286f253..df2cdc599 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardNewRecordByColumnIdComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardNewRecordByColumnIdComponentFamilyState.ts @@ -1,4 +1,4 @@ -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; export type NewCard = { @@ -7,7 +7,7 @@ export type NewCard = { isCreating: boolean; position: 'first' | 'last'; isOpportunity: boolean; - company: EntityForSelect | null; + company: RecordForSelect | null; }; export const recordBoardNewRecordByColumnIdComponentFamilyState = diff --git a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts index d12e1b73b..c51f0c95f 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/hooks/usePersistField.ts @@ -24,7 +24,7 @@ import { isFieldRelationToOneValue } from '@/object-record/record-field/types/gu import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelectValue } from '@/object-record/record-field/types/guards/isFieldSelectValue'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { isFieldArray } from '@/object-record/record-field/types/guards/isFieldArray'; import { isFieldArrayValue } from '@/object-record/record-field/types/guards/isFieldArrayValue'; @@ -141,7 +141,7 @@ export const usePersistField = () => { ); if (fieldIsRelationToOneObject) { - const value = valueToPersist as EntityForSelect; + const value = valueToPersist as RecordForSelect; updateRecord?.({ variables: { where: { id: recordId }, diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay.tsx index 48e83e11c..cfa51862f 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/display/components/RelationFromManyFieldDisplay.tsx @@ -14,7 +14,7 @@ export const RelationFromManyFieldDisplay = () => { const { fieldName, objectMetadataNameSingular } = fieldDefinition.metadata; - const relationObjectNameSingular = + const objectNameSingular = fieldDefinition?.metadata.relationObjectMetadataNameSingular; const { activityTargetObjectRecords } = useActivityTargetObjectRecords( @@ -22,7 +22,7 @@ export const RelationFromManyFieldDisplay = () => { fieldValue as NoteTarget[] | TaskTarget[], ); - if (!fieldValue || !relationObjectNameSingular) { + if (!fieldValue || !objectNameSingular) { return null; } @@ -81,7 +81,7 @@ export const RelationFromManyFieldDisplay = () => { .map((record) => ( ))} diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRelationField.ts b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRelationField.ts index 3f2cee4a5..f3b40a23b 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRelationField.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/hooks/useRelationField.ts @@ -5,7 +5,7 @@ import { useGetButtonIcon } from '@/object-record/record-field/hooks/useGetButto import { useRecordFieldInput } from '@/object-record/record-field/hooks/useRecordFieldInput'; import { FieldRelationValue } from '@/object-record/record-field/types/FieldMetadata'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldContext } from '../../contexts/FieldContext'; @@ -13,7 +13,7 @@ import { assertFieldMetadata } from '../../types/guards/assertFieldMetadata'; import { isFieldRelation } from '../../types/guards/isFieldRelation'; export const useRelationField = < - T extends EntityForSelect | EntityForSelect[], + T extends RecordForSelect | RecordForSelect[], >() => { const { recordId, fieldDefinition, maxWidth } = useContext(FieldContext); const button = useGetButtonIcon(); diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput.tsx index d2ce24f8c..a13f9e5c3 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput.tsx @@ -9,7 +9,7 @@ import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEv import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; type RelationFromManyFieldInputProps = { @@ -20,9 +20,9 @@ export const RelationFromManyFieldInput = ({ onSubmit, }: RelationFromManyFieldInputProps) => { const { fieldDefinition, recordId } = useContext(FieldContext); - const relationPickerScopeId = `relation-picker-${fieldDefinition.fieldMetadataId}`; + const recordPickerInstanceId = `record-picker-${fieldDefinition.fieldMetadataId}`; const { updateRelation } = useUpdateRelationFromManyFieldInput({ - scopeId: relationPickerScopeId, + scopeId: recordPickerInstanceId, }); const handleSubmit = () => { @@ -51,11 +51,13 @@ export const RelationFromManyFieldInput = ({ recordId, }); - const { dropdownPlacement } = useDropdown(relationPickerScopeId); + const { dropdownPlacement } = useDropdown(recordPickerInstanceId); return ( <> - + - + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx index 866edfdad..6ba293fd9 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationFromManyFieldInputMultiRecordsEffect.tsx @@ -5,28 +5,28 @@ import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useOb import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useRelationField } from '@/object-record/record-field/meta-types/hooks/useRelationField'; import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; -import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { useRecordPickerRecordsOptions } from '@/object-record/relation-picker/hooks/useRecordPickerRecordsOptions'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; export const RelationFromManyFieldInputMultiRecordsEffect = () => { - const { fieldValue, fieldDefinition } = useRelationField(); - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, + const { fieldValue, fieldDefinition } = useRelationField(); + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordPickerComponentInstanceContext, ); const { objectRecordsIdsMultiSelectState, objectRecordMultiSelectCheckedRecordsIdsState, recordMultiSelectIsLoadingState, - } = useObjectRecordMultiSelectScopedStates(scopeId); + } = useObjectRecordMultiSelectScopedStates(instanceId); const [objectRecordsIdsMultiSelect, setObjectRecordsIdsMultiSelect] = useRecoilState(objectRecordsIdsMultiSelectState); - const { entities } = useRelationPickerEntitiesOptions({ - relationObjectNameSingular: + const { records } = useRecordPickerRecordsOptions({ + objectNameSingular: fieldDefinition.metadata.relationObjectMetadataNameSingular, }); @@ -41,7 +41,7 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { const allRecords = useMemo( () => [ - ...entities.entitiesToSelect.map((entity) => { + ...records.recordsToSelect.map((entity) => { const { record, ...recordIdentifier } = entity; return { objectMetadataItem: objectMetadataItem, @@ -50,7 +50,7 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { }; }), ], - [entities.entitiesToSelect, objectMetadataItem], + [records.recordsToSelect, objectMetadataItem], ); const [ @@ -65,7 +65,7 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { const currentRecord = snapshot .getLoadable( objectRecordMultiSelectComponentFamilyState({ - scopeId: scopeId, + scopeId: instanceId, familyKey: newRecord.record.id, }), ) @@ -86,7 +86,7 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { ) { set( objectRecordMultiSelectComponentFamilyState({ - scopeId: scopeId, + scopeId: instanceId, familyKey: newRecordWithSelected.record.id, }), newRecordWithSelected, @@ -94,7 +94,7 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { } } }, - [objectRecordMultiSelectCheckedRecordsIds, scopeId], + [objectRecordMultiSelectCheckedRecordsIds, instanceId], ); useEffect(() => { @@ -113,14 +113,14 @@ export const RelationFromManyFieldInputMultiRecordsEffect = () => { useEffect(() => { setObjectRecordMultiSelectCheckedRecordsIds( fieldValue - ? fieldValue.map((fieldValueItem: EntityForSelect) => fieldValueItem.id) + ? fieldValue.map((fieldValueItem: RecordForSelect) => fieldValueItem.id) : [], ); }, [fieldValue, setObjectRecordMultiSelectCheckedRecordsIds]); useEffect(() => { - setRecordMultiSelectIsLoading(entities.loading); - }, [entities.loading, setRecordMultiSelectIsLoading]); + setRecordMultiSelectIsLoading(records.loading); + }, [records.loading, setRecordMultiSelectIsLoading]); return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationToOneFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationToOneFieldInput.tsx index e901bfe74..3b9e388a3 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationToOneFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/RelationToOneFieldInput.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import { RelationPicker } from '@/object-record/relation-picker/components/RelationPicker'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { usePersistField } from '../../../hooks/usePersistField'; import { useRelationField } from '../../hooks/useRelationField'; @@ -24,11 +24,11 @@ export const RelationToOneFieldInput = ({ onCancel, }: RelationToOneFieldInputProps) => { const { fieldDefinition, initialSearchValue, fieldValue } = - useRelationField(); + useRelationField(); const persistField = usePersistField(); - const handleSubmit = (newEntity: EntityForSelect | null) => { + const handleSubmit = (newEntity: RecordForSelect | null) => { onSubmit?.(() => persistField(newEntity?.record ?? null)); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx index 86bea16ad..3c3cf67ba 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/SelectFieldInput.tsx @@ -1,7 +1,7 @@ import { useClearField } from '@/object-record/record-field/hooks/useClearField'; import { useSelectField } from '@/object-record/record-field/meta-types/hooks/useSelectField'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; -import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList'; +import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList'; import { SelectOption } from '@/spreadsheet-import/types'; import { SelectInput } from '@/ui/input/components/SelectInput'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; @@ -28,7 +28,7 @@ export const SelectFieldInput = ({ const [filteredOptions, setFilteredOptions] = useState([]); const { resetSelectedItem } = useSelectableList( - SINGLE_ENTITY_SELECT_BASE_LIST, + SINGLE_RECORD_SELECT_BASE_LIST, ); const clearField = useClearField(); @@ -65,7 +65,7 @@ export const SelectFieldInput = ({ return (
{ diff --git a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx index 6bb6f02d2..8d01e3d98 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/meta-types/input/components/__stories__/RelationToOneFieldInput.stories.tsx @@ -13,7 +13,6 @@ import { useSetRecoilState } from 'recoil'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { FieldMetadataType } from '~/generated/graphql'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; @@ -26,6 +25,7 @@ import { } from '~/testing/mock-data/users'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { RelationToOneFieldInput, RelationToOneFieldInputProps, @@ -80,12 +80,12 @@ const RelationToOneFieldInputWithContext = ({ }} recordId={recordId} > - - +
diff --git a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts index f739af431..1380e5855 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/record-field/types/FieldMetadata.ts @@ -2,7 +2,7 @@ import { ThemeColor } from 'twenty-ui'; import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues'; import { ZodHelperLiteral } from '@/object-record/record-field/types/ZodHelperLiteral'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { RelationDefinitionType } from '~/generated-metadata/graphql'; import { CurrencyCode } from './CurrencyCode'; @@ -244,9 +244,9 @@ export type FieldRatingValue = (typeof RATING_VALUES)[number] | null; export type FieldSelectValue = string | null; export type FieldMultiSelectValue = string[] | null; -export type FieldRelationToOneValue = EntityForSelect | null; +export type FieldRelationToOneValue = RecordForSelect | null; -export type FieldRelationFromManyValue = EntityForSelect[] | []; +export type FieldRelationFromManyValue = RecordForSelect[] | []; export type FieldRelationValue< T extends FieldRelationToOneValue | FieldRelationFromManyValue, diff --git a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx index 1420ce51a..9dfce6d33 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/record-detail-section/components/RecordDetailRelationSection.tsx @@ -18,11 +18,11 @@ import { RecordDetailSectionHeader } from '@/object-record/record-show/record-de import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { MultiRecordSelect } from '@/object-record/relation-picker/components/MultiRecordSelect'; -import { SingleEntitySelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch'; +import { SingleRecordSelectMenuItemsWithSearch } from '@/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; -import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { usePrefetchedData } from '@/prefetch/hooks/usePrefetchedData'; import { PrefetchKey } from '@/prefetch/types/PrefetchKey'; @@ -84,13 +84,13 @@ export const RecordDetailRelationSection = ({ const { closeDropdown, isDropdownOpen, dropdownPlacement } = useDropdown(dropdownId); - const { setRelationPickerSearchFilter } = useRelationPicker({ - relationPickerScopeId: dropdownId, + const { setRecordPickerSearchFilter } = useRecordPicker({ + recordPickerInstanceId: dropdownId, }); const handleCloseRelationPickerDropdown = useCallback(() => { - setRelationPickerSearchFilter(''); - }, [setRelationPickerSearchFilter]); + setRecordPickerSearchFilter(''); + }, [setRecordPickerSearchFilter]); const persistField = usePersistField(); const { updateOneRecord: updateOneRelationRecord } = useUpdateOneRecord({ @@ -98,7 +98,7 @@ export const RecordDetailRelationSection = ({ }); const handleRelationPickerEntitySelected = ( - selectedRelationEntity?: EntityForSelect, + selectedRelationEntity?: RecordForSelect, ) => { closeDropdown(); @@ -193,16 +193,16 @@ export const RecordDetailRelationSection = ({ /> } dropdownComponents={ - + {isToOneObject ? ( - @@ -217,7 +217,7 @@ export const RecordDetailRelationSection = ({ /> )} - + } dropdownHotkeyScope={{ scope: dropdownId, diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx index 26981f3c0..e95b0aeaa 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsEffect.tsx @@ -9,10 +9,10 @@ import { import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates'; import { objectRecordMultiSelectComponentFamilyState } from '@/object-record/record-field/states/objectRecordMultiSelectComponentFamilyState'; import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { ObjectRecordForSelect } from '@/object-record/types/ObjectRecordForSelect'; import { SelectedObjectRecordId } from '@/object-record/types/SelectedObjectRecordId'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ @@ -20,13 +20,13 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ }: { selectedObjectRecordIds: SelectedObjectRecordId[]; }) => { - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordPickerComponentInstanceContext, ); const { objectRecordsIdsMultiSelectState, objectRecordMultiSelectCheckedRecordsIdsState, - } = useObjectRecordMultiSelectScopedStates(scopeId); + } = useObjectRecordMultiSelectScopedStates(instanceId); const [objectRecordsIdsMultiSelect, setObjectRecordsIdsMultiSelect] = useRecoilState(objectRecordsIdsMultiSelectState); @@ -41,7 +41,7 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ const currentRecord = snapshot .getLoadable( objectRecordMultiSelectComponentFamilyState({ - scopeId: scopeId, + scopeId: instanceId, familyKey: newRecord.record.id, }), ) @@ -66,7 +66,7 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ ) { set( objectRecordMultiSelectComponentFamilyState({ - scopeId: scopeId, + scopeId: instanceId, familyKey: newRecordWithSelected.record.id, }), newRecordWithSelected, @@ -74,12 +74,12 @@ export const ActivityTargetInlineCellEditModeMultiRecordsEffect = ({ } } }, - [objectRecordMultiSelectCheckedRecordsIdsState, scopeId], + [objectRecordMultiSelectCheckedRecordsIdsState, instanceId], ); const matchesSearchFilterObjectRecords = useRecoilValue( objectRecordMultiSelectMatchesFilterRecordsIdsComponentState({ - scopeId, + scopeId: instanceId, }), ); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx index 347bc81b7..876c02865 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect.tsx @@ -1,35 +1,29 @@ import { useEffect } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useSetRecoilState } from 'recoil'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { objectRecordMultiSelectMatchesFilterRecordsIdsComponentState } from '@/object-record/record-field/states/objectRecordMultiSelectMatchesFilterRecordsIdsComponentState'; -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; import { useMultiObjectSearch } from '@/object-record/relation-picker/hooks/useMultiObjectSearch'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect = () => { - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordPickerComponentInstanceContext, ); - const setRecordMultiSelectMatchesFilterRecords = useSetRecoilState( objectRecordMultiSelectMatchesFilterRecordsIdsComponentState({ - scopeId, + scopeId: instanceId, }), ); - const relationPickerScopedId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, - ); - - const { relationPickerSearchFilterState } = useRelationPickerScopedStates({ - relationPickerScopedId, - }); - const relationPickerSearchFilter = useRecoilValue( - relationPickerSearchFilterState, + const recordPickerSearchFilter = useRecoilComponentValueV2( + recordPickerSearchFilterComponentState, + instanceId, ); const { matchesSearchFilterObjectRecordsQueryResult } = @@ -38,7 +32,7 @@ export const ActivityTargetInlineCellEditModeMultiRecordsSearchFilterEffect = CoreObjectNameSingular.Task, CoreObjectNameSingular.Note, ], - searchFilterValue: relationPickerSearchFilter, + searchFilterValue: recordPickerSearchFilter, limit: 10, }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx index e80670197..278e0e568 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultiRecordSelect.tsx @@ -2,8 +2,8 @@ import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useOb import { MultipleObjectRecordOnClickOutsideEffect } from '@/object-record/relation-picker/components/MultipleObjectRecordOnClickOutsideEffect'; import { MultipleObjectRecordSelectItem } from '@/object-record/relation-picker/components/MultipleObjectRecordSelectItem'; import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId'; -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; @@ -16,11 +16,13 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import styled from '@emotion/styled'; import { Placement } from '@floating-ui/react'; import { useCallback, useEffect, useRef } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { Key } from 'ts-key-enum'; import { IconPlus, isDefined } from 'twenty-ui'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; @@ -45,12 +47,12 @@ export const MultiRecordSelect = ({ const setHotkeyScope = useSetHotkeyScope(); const { goBackToPreviousHotkeyScope } = usePreviousHotkeyScope(); - const relationPickerScopedId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordPickerComponentInstanceContext, ); const { objectRecordsIdsMultiSelectState, recordMultiSelectIsLoadingState } = - useObjectRecordMultiSelectScopedStates(relationPickerScopedId); + useObjectRecordMultiSelectScopedStates(instanceId); const { resetSelectedItem } = useSelectableList( MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID, @@ -63,18 +65,18 @@ export const MultiRecordSelect = ({ objectRecordsIdsMultiSelectState, ); - const { relationPickerSearchFilterState } = useRelationPickerScopedStates({ - relationPickerScopedId, - }); - - const setSearchFilter = useSetRecoilState(relationPickerSearchFilterState); - const relationPickerSearchFilter = useRecoilValue( - relationPickerSearchFilterState, + const setSearchFilter = useSetRecoilComponentStateV2( + recordPickerSearchFilterComponentState, + instanceId, + ); + const recordPickerSearchFilter = useRecoilComponentValueV2( + recordPickerSearchFilterComponentState, + instanceId, ); useEffect(() => { - setHotkeyScope(relationPickerScopedId); - }, [setHotkeyScope, relationPickerScopedId]); + setHotkeyScope(instanceId); + }, [setHotkeyScope, instanceId]); useScopedHotkeys( Key.Escape, @@ -83,7 +85,7 @@ export const MultiRecordSelect = ({ goBackToPreviousHotkeyScope(); resetSelectedItem(); }, - relationPickerScopedId, + instanceId, [onSubmit, goBackToPreviousHotkeyScope, resetSelectedItem], ); @@ -99,7 +101,7 @@ export const MultiRecordSelect = ({ { onChange?.(selectedId); resetSelectedItem(); @@ -123,7 +125,7 @@ export const MultiRecordSelect = ({ const createNewButton = isDefined(onCreate) && ( onCreate?.(relationPickerSearchFilter)} + onClick={() => onCreate?.(recordPickerSearchFilter)} LeftIcon={IconPlus} text="Add New" /> @@ -147,7 +149,7 @@ export const MultiRecordSelect = ({ )} {objectRecordsIdsMultiSelect?.length > 0 && results} - {recordMultiSelectIsLoading && !relationPickerSearchFilter && ( + {recordMultiSelectIsLoading && !recordPickerSearchFilter && ( <> @@ -159,7 +161,7 @@ export const MultiRecordSelect = ({ )} @@ -167,7 +169,7 @@ export const MultiRecordSelect = ({ isUndefinedOrNull(dropdownPlacement)) && ( <> - {recordMultiSelectIsLoading && !relationPickerSearchFilter && ( + {recordMultiSelectIsLoading && !recordPickerSearchFilter && ( <> diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx index dbc0302b9..ce8d472bd 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/MultipleObjectRecordSelectItem.tsx @@ -4,10 +4,10 @@ import { Avatar, MenuItemMultiSelectAvatar } from 'twenty-ui'; import { useObjectRecordMultiSelectScopedStates } from '@/activities/hooks/useObjectRecordMultiSelectScopedStates'; import { MULTI_OBJECT_RECORD_SELECT_SELECTABLE_LIST_ID } from '@/object-record/relation-picker/constants/MultiObjectRecordSelectSelectableListId'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { isDefined } from '~/utils/isDefined'; export const StyledSelectableItem = styled(SelectableItem)` @@ -29,14 +29,14 @@ export const MultipleObjectRecordSelectItem = ({ const isSelectedByKeyboard = useRecoilValue( isSelectedItemIdSelector(objectRecordId), ); - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, + const instanceId = useAvailableComponentInstanceIdOrThrow( + RecordPickerComponentInstanceContext, ); const { objectRecordMultiSelectFamilyState, objectRecordMultiSelectCheckedRecordsIdsState, - } = useObjectRecordMultiSelectScopedStates(scopeId); + } = useObjectRecordMultiSelectScopedStates(instanceId); const record = useRecoilValue( objectRecordMultiSelectFamilyState(objectRecordId), diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx index fb6293418..cdeb25c68 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/RelationPicker.tsx @@ -6,16 +6,16 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldRelationMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { SearchPickerInitialValueEffect } from '@/object-record/relation-picker/components/SearchPickerInitialValueEffect'; -import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; +import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect'; import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; export type RelationPickerProps = { selectedRecordId?: string; - onSubmit: (selectedEntity: EntityForSelect | null) => void; + onSubmit: (selectedRecord: RecordForSelect | null) => void; onCancel?: () => void; width?: number; - excludeRecordIds?: string[]; + excludedRecordIds?: string[]; initialSearchFilter?: string | null; fieldDefinition: FieldDefinition; }; @@ -24,16 +24,16 @@ export const RelationPicker = ({ selectedRecordId, onSubmit, onCancel, - excludeRecordIds, + excludedRecordIds, width, initialSearchFilter, fieldDefinition, }: RelationPickerProps) => { - const relationPickerScopeId = 'relation-picker'; + const recordPickerInstanceId = 'relation-picker'; - const handleEntitySelected = ( - selectedEntity: EntityForSelect | null | undefined, - ) => onSubmit(selectedEntity ?? null); + const handleRecordSelected = ( + selectedRecord: RecordForSelect | null | undefined, + ) => onSubmit(selectedRecord ?? null); const { objectMetadataItem: relationObjectMetadataItem } = useObjectMetadataItem({ @@ -60,21 +60,21 @@ export const RelationPicker = ({ <> - ); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SearchPickerInitialValueEffect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SearchPickerInitialValueEffect.tsx index 284de747c..85e2206ae 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SearchPickerInitialValueEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SearchPickerInitialValueEffect.tsx @@ -1,25 +1,22 @@ -import { getRelationPickerScopedStates } from '@/object-record/relation-picker/utils/getRelationPickerScopedStates'; +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useEffect } from 'react'; -import { useSetRecoilState } from 'recoil'; export const SearchPickerInitialValueEffect = ({ initialValueForSearchFilter, - relationPickerScopeId, + recordPickerInstanceId, }: { initialValueForSearchFilter?: string | null; - relationPickerScopeId: string; + recordPickerInstanceId: string; }) => { - const { relationPickerSearchFilterState } = getRelationPickerScopedStates({ - relationPickerScopeId: relationPickerScopeId, - }); - - const setRelationPickerSearchFilter = useSetRecoilState( - relationPickerSearchFilterState, + const setRecordPickerSearchFilter = useSetRecoilComponentStateV2( + recordPickerSearchFilterComponentState, + recordPickerInstanceId, ); useEffect(() => { - setRelationPickerSearchFilter(initialValueForSearchFilter ?? ''); - }, [initialValueForSearchFilter, setRelationPickerSearchFilter]); + setRecordPickerSearchFilter(initialValueForSearchFilter ?? ''); + }, [initialValueForSearchFilter, setRecordPickerSearchFilter]); return <>; }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx index 986a570e8..cb1019050 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SelectableMenuItemSelect.tsx @@ -2,14 +2,15 @@ import styled from '@emotion/styled'; import { useRecoilValue } from 'recoil'; import { Avatar, MenuItemSelectAvatar } from 'twenty-ui'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; import { SelectableItem } from '@/ui/layout/selectable-list/components/SelectableItem'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; type SelectableMenuItemSelectProps = { - entity: EntityForSelect; - onEntitySelected: (entitySelected?: EntityForSelect) => void; - selectedEntity?: EntityForSelect; + record: RecordForSelect; + onRecordSelected: (recordSelected?: RecordForSelect) => void; + selectedRecord?: RecordForSelect; }; const StyledSelectableItem = styled(SelectableItem)` @@ -17,31 +18,31 @@ const StyledSelectableItem = styled(SelectableItem)` `; export const SelectableMenuItemSelect = ({ - entity, - onEntitySelected, - selectedEntity, + record, + onRecordSelected, + selectedRecord, }: SelectableMenuItemSelectProps) => { const { isSelectedItemIdSelector } = useSelectableList( - 'single-entity-select-base-list', + SINGLE_RECORD_SELECT_BASE_LIST, ); - const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(entity.id)); + const isSelectedItemId = useRecoilValue(isSelectedItemIdSelector(record.id)); return ( - + onEntitySelected(entity)} - text={entity.name} - selected={selectedEntity?.id === entity.id} + onClick={() => onRecordSelected(record)} + text={record.name} + selected={selectedRecord?.id === record.id} hovered={isSelectedItemId} avatar={ } /> diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx deleted file mode 100644 index 6654da7ec..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { - SingleEntitySelectMenuItems, - SingleEntitySelectMenuItemsProps, -} from '@/object-record/relation-picker/components/SingleEntitySelectMenuItems'; -import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; -import { useRelationPickerEntitiesOptions } from '@/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions'; -import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; -import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; -import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; -import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; -import { Placement } from '@floating-ui/react'; -import { IconPlus } from 'twenty-ui'; -import { isDefined } from '~/utils/isDefined'; -import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; - -export type SingleEntitySelectMenuItemsWithSearchProps = { - excludedRelationRecordIds?: string[]; - onCreate?: ((searchInput?: string) => void) | (() => void); - relationObjectNameSingular: string; - relationPickerScopeId?: string; - selectedRelationRecordIds: string[]; - dropdownPlacement?: Placement | null; -} & Pick< - SingleEntitySelectMenuItemsProps, - | 'EmptyIcon' - | 'emptyLabel' - | 'onCancel' - | 'onEntitySelected' - | 'selectedEntity' ->; - -export const SingleEntitySelectMenuItemsWithSearch = ({ - EmptyIcon, - emptyLabel, - excludedRelationRecordIds, - onCancel, - onCreate, - onEntitySelected, - relationObjectNameSingular, - relationPickerScopeId = 'relation-picker', - selectedRelationRecordIds, - dropdownPlacement, -}: SingleEntitySelectMenuItemsWithSearchProps) => { - const { handleSearchFilterChange } = useEntitySelectSearch({ - relationPickerScopeId, - }); - - const { entities, relationPickerSearchFilter } = - useRelationPickerEntitiesOptions({ - relationObjectNameSingular, - selectedRelationRecordIds, - excludedRelationRecordIds, - }); - - const createNewButton = isDefined(onCreate) && ( - onCreate?.(relationPickerSearchFilter)} - LeftIcon={IconPlus} - text="Add New" - /> - ); - - const results = ( - - ); - - return ( - <> - {dropdownPlacement?.includes('end') && ( - <> - - {createNewButton} - - {entities.entitiesToSelect.length > 0 && } - {entities.entitiesToSelect.length > 0 && results} - - - )} - - {(dropdownPlacement?.includes('start') || - isUndefinedOrNull(dropdownPlacement)) && ( - <> - - {entities.entitiesToSelect.length > 0 && results} - {entities.entitiesToSelect.length > 0 && isDefined(onCreate) && ( - - )} - {isDefined(onCreate) && ( - - {createNewButton} - - )} - - )} - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelect.tsx similarity index 63% rename from packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx rename to packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelect.tsx index b98c3f36b..221fa3961 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelect.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelect.tsx @@ -1,31 +1,31 @@ import { useRef } from 'react'; import { - SingleEntitySelectMenuItemsWithSearch, - SingleEntitySelectMenuItemsWithSearchProps, -} from '@/object-record/relation-picker/components/SingleEntitySelectMenuItemsWithSearch'; + SingleRecordSelectMenuItemsWithSearch, + SingleRecordSelectMenuItemsWithSearchProps, +} from '@/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { isDefined } from '~/utils/isDefined'; -export type SingleEntitySelectProps = { +export type SingleRecordSelectProps = { disableBackgroundBlur?: boolean; width?: number; -} & SingleEntitySelectMenuItemsWithSearchProps; +} & SingleRecordSelectMenuItemsWithSearchProps; -export const SingleEntitySelect = ({ +export const SingleRecordSelect = ({ disableBackgroundBlur = false, EmptyIcon, emptyLabel, - excludedRelationRecordIds, + excludedRecordIds, onCancel, onCreate, - onEntitySelected, - relationObjectNameSingular, - relationPickerScopeId, - selectedRelationRecordIds, + onRecordSelected, + objectNameSingular, + recordPickerInstanceId, + selectedRecordIds, width = 200, -}: SingleEntitySelectProps) => { +}: SingleRecordSelectProps) => { const containerRef = useRef(null); useListenClickOutside({ @@ -50,17 +50,17 @@ export const SingleEntitySelect = ({ width={width} data-select-disable > - diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx similarity index 66% rename from packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx rename to packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx index 0e8a7b1e3..c657ba9a9 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleEntitySelectMenuItems.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItems.tsx @@ -5,7 +5,7 @@ import { Key } from 'ts-key-enum'; import { IconComponent, MenuItemSelect } from 'twenty-ui'; import { SelectableMenuItemSelect } from '@/object-record/relation-picker/components/SelectableMenuItemSelect'; -import { SINGLE_ENTITY_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleEntitySelectBaseList'; +import { SINGLE_RECORD_SELECT_BASE_LIST } from '@/object-record/relation-picker/constants/SingleRecordSelectBaseList'; import { DropdownMenuSkeletonItem } from '@/ui/input/relation-picker/components/skeletons/DropdownMenuSkeletonItem'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList'; @@ -13,44 +13,44 @@ import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectab import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { isDefined } from '~/utils/isDefined'; -import { EntityForSelect } from '../types/EntityForSelect'; +import { RecordForSelect } from '../types/RecordForSelect'; import { RelationPickerHotkeyScope } from '../types/RelationPickerHotkeyScope'; -export type SingleEntitySelectMenuItemsProps = { +export type SingleRecordSelectMenuItemsProps = { EmptyIcon?: IconComponent; emptyLabel?: string; - entitiesToSelect: EntityForSelect[]; + recordsToSelect: RecordForSelect[]; loading?: boolean; onCancel?: () => void; - onEntitySelected: (entity?: EntityForSelect) => void; - selectedEntity?: EntityForSelect; + onRecordSelected: (entity?: RecordForSelect) => void; + selectedRecord?: RecordForSelect; SelectAllIcon?: IconComponent; selectAllLabel?: string; - isAllEntitySelected?: boolean; - isAllEntitySelectShown?: boolean; - onAllEntitySelected?: () => void; + isAllRecordsSelected?: boolean; + isAllRecordsSelectShown?: boolean; + onAllRecordsSelected?: () => void; hotkeyScope?: string; isFiltered: boolean; shouldSelectEmptyOption?: boolean; }; -export const SingleEntitySelectMenuItems = ({ +export const SingleRecordSelectMenuItems = ({ EmptyIcon, emptyLabel, - entitiesToSelect, + recordsToSelect, loading, onCancel, - onEntitySelected, - selectedEntity, + onRecordSelected, + selectedRecord, SelectAllIcon, selectAllLabel, - isAllEntitySelected, - isAllEntitySelectShown, - onAllEntitySelected, + isAllRecordsSelected, + isAllRecordsSelectShown, + onAllRecordsSelected, hotkeyScope = RelationPickerHotkeyScope.RelationPicker, isFiltered, shouldSelectEmptyOption, -}: SingleEntitySelectMenuItemsProps) => { +}: SingleRecordSelectMenuItemsProps) => { const containerRef = useRef(null); const selectNone = emptyLabel @@ -61,7 +61,7 @@ export const SingleEntitySelectMenuItems = ({ } : null; - const selectAll = isAllEntitySelectShown + const selectAll = isAllRecordsSelectShown ? { __typename: '', id: 'select-all', @@ -69,18 +69,18 @@ export const SingleEntitySelectMenuItems = ({ } : null; - const entitiesInDropdown = [ + const recordsInDropdown = [ selectAll, selectNone, - selectedEntity, - ...entitiesToSelect, + selectedRecord, + ...recordsToSelect, ].filter( - (entity): entity is EntityForSelect => + (entity): entity is RecordForSelect => isDefined(entity) && isNonEmptyString(entity.name), ); const { isSelectedItemIdSelector, resetSelectedItem } = useSelectableList( - SINGLE_ENTITY_SELECT_BASE_LIST, + SINGLE_RECORD_SELECT_BASE_LIST, ); const isSelectedSelectNoneButton = useRecoilValue( @@ -101,38 +101,38 @@ export const SingleEntitySelectMenuItems = ({ [onCancel, resetSelectedItem], ); - const selectableItemIds = entitiesInDropdown.map((entity) => entity.id); + const selectableItemIds = recordsInDropdown.map((entity) => entity.id); return (
{ - const entityIndex = entitiesInDropdown.findIndex( - (entity) => entity.id === itemId, + const recordIndex = recordsInDropdown.findIndex( + (record) => record.id === itemId, ); - onEntitySelected(entitiesInDropdown[entityIndex]); + onRecordSelected(recordsInDropdown[recordIndex]); resetSelectedItem(); }} > {loading && !isFiltered ? ( - ) : entitiesInDropdown.length === 0 && - !isAllEntitySelectShown && + ) : recordsInDropdown.length === 0 && + !isAllRecordsSelectShown && !loading ? ( <> ) : ( - entitiesInDropdown?.map((entity) => { - switch (entity.id) { + recordsInDropdown?.map((record) => { + switch (record.id) { case 'select-none': { return ( emptyLabel && ( onEntitySelected()} + key={record.id} + onClick={() => onRecordSelected()} LeftIcon={EmptyIcon} text={emptyLabel} selected={shouldSelectEmptyOption === true} @@ -143,15 +143,15 @@ export const SingleEntitySelectMenuItems = ({ } case 'select-all': { return ( - isAllEntitySelectShown && + isAllRecordsSelectShown && selectAllLabel && - onAllEntitySelected && ( + onAllRecordsSelected && ( onAllEntitySelected()} + key={record.id} + onClick={() => onAllRecordsSelected()} LeftIcon={SelectAllIcon} text={selectAllLabel} - selected={!!isAllEntitySelected} + selected={!!isAllRecordsSelected} hovered={isSelectedSelectAllButton} /> ) @@ -160,10 +160,10 @@ export const SingleEntitySelectMenuItems = ({ default: { return ( ); } diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch.tsx new file mode 100644 index 000000000..4eb6ac108 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/SingleRecordSelectMenuItemsWithSearch.tsx @@ -0,0 +1,113 @@ +import { + SingleRecordSelectMenuItems, + SingleRecordSelectMenuItemsProps, +} from '@/object-record/relation-picker/components/SingleRecordSelectMenuItems'; +import { useRecordPickerRecordsOptions } from '@/object-record/relation-picker/hooks/useRecordPickerRecordsOptions'; +import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch'; +import { CreateNewButton } from '@/ui/input/relation-picker/components/CreateNewButton'; +import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput'; +import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; +import { Placement } from '@floating-ui/react'; +import { IconPlus } from 'twenty-ui'; +import { isDefined } from '~/utils/isDefined'; +import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; + +export type SingleRecordSelectMenuItemsWithSearchProps = { + excludedRecordIds?: string[]; + onCreate?: ((searchInput?: string) => void) | (() => void); + objectNameSingular: string; + recordPickerInstanceId?: string; + selectedRecordIds: string[]; + dropdownPlacement?: Placement | null; +} & Pick< + SingleRecordSelectMenuItemsProps, + | 'EmptyIcon' + | 'emptyLabel' + | 'onCancel' + | 'onRecordSelected' + | 'selectedRecord' +>; + +export const SingleRecordSelectMenuItemsWithSearch = ({ + EmptyIcon, + emptyLabel, + excludedRecordIds, + onCancel, + onCreate, + onRecordSelected, + objectNameSingular, + recordPickerInstanceId = 'record-picker', + selectedRecordIds, + dropdownPlacement, +}: SingleRecordSelectMenuItemsWithSearchProps) => { + const { handleSearchFilterChange } = useRecordSelectSearch({ + recordPickerInstanceId, + }); + + const { records, recordPickerSearchFilter } = useRecordPickerRecordsOptions({ + objectNameSingular, + selectedRecordIds, + excludedRecordIds, + }); + + const createNewButton = isDefined(onCreate) && ( + onCreate?.(recordPickerSearchFilter)} + LeftIcon={IconPlus} + text="Add New" + /> + ); + + const results = ( + + ); + + return ( + <> + {dropdownPlacement?.includes('end') && ( + <> + + {createNewButton} + + {records.recordsToSelect.length > 0 && } + {records.recordsToSelect.length > 0 && results} + + + )} + + {(dropdownPlacement?.includes('start') || + isUndefinedOrNull(dropdownPlacement)) && ( + <> + + {records.recordsToSelect.length > 0 && results} + {records.recordsToSelect.length > 0 && isDefined(onCreate) && ( + + )} + {isDefined(onCreate) && ( + + {createNewButton} + + )} + + )} + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleRecordSelect.stories.tsx similarity index 69% rename from packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx rename to packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleRecordSelect.stories.tsx index 683c83459..fb445e84b 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleEntitySelect.stories.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/components/__stories__/SingleRecordSelect.stories.tsx @@ -5,18 +5,18 @@ import { ComponentDecorator, IconUserCircle } from 'twenty-ui'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; -import { RelationPickerDecorator } from '~/testing/decorators/RelationPickerDecorator'; +import { RecordPickerDecorator } from '~/testing/decorators/RecordPickerDecorator'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { getPeopleMock } from '~/testing/mock-data/people'; import { sleep } from '~/utils/sleep'; -import { EntityForSelect } from '../../types/EntityForSelect'; -import { SingleEntitySelect } from '../SingleEntitySelect'; +import { RecordForSelect } from '../../types/RecordForSelect'; +import { SingleRecordSelect } from '../SingleRecordSelect'; const peopleMock = getPeopleMock(); -const entities = peopleMock.map((person) => ({ +const records = peopleMock.map((person) => ({ id: person.id, name: person.name.firstName + ' ' + person.name.lastName, avatarUrl: 'https://picsum.photos/200', @@ -24,24 +24,24 @@ const entities = peopleMock.map((person) => ({ record: { ...person, __typename: 'Person' }, })); -const meta: Meta = { - title: 'UI/Input/RelationPicker/SingleEntitySelect', - component: SingleEntitySelect, +const meta: Meta = { + title: 'UI/Input/RelationPicker/SingleRecordSelect', + component: SingleRecordSelect, decorators: [ ComponentDecorator, ComponentWithRecoilScopeDecorator, - RelationPickerDecorator, + RecordPickerDecorator, ObjectMetadataItemsDecorator, SnackBarDecorator, ], args: { - relationObjectNameSingular: CoreObjectNameSingular.WorkspaceMember, - selectedRelationRecordIds: [], + objectNameSingular: CoreObjectNameSingular.WorkspaceMember, + selectedRecordIds: [], }, argTypes: { - selectedEntity: { - options: entities.map(({ name }) => name), - mapping: entities.reduce( + selectedRecord: { + options: records.map(({ name }) => name), + mapping: records.reduce( (result, entity) => ({ ...result, [entity.name]: entity }), {}, ), @@ -53,12 +53,12 @@ const meta: Meta = { }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = {}; -export const WithSelectedEntity: Story = { - args: { selectedEntity: entities[2] }, +export const WithSelectedRecord: Story = { + args: { selectedRecord: records[2] }, }; export const WithEmptyOption: Story = { diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts b/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts deleted file mode 100644 index 3aead1418..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleEntitySelectBaseList.ts +++ /dev/null @@ -1 +0,0 @@ -export const SINGLE_ENTITY_SELECT_BASE_LIST = 'single-entity-select-base-list'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleRecordSelectBaseList.ts b/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleRecordSelectBaseList.ts new file mode 100644 index 000000000..7f36c7bd0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/constants/SingleRecordSelectBaseList.ts @@ -0,0 +1 @@ +export const SINGLE_RECORD_SELECT_BASE_LIST = 'single-record-select-base-list'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useEntitySelectSearch.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useEntitySelectSearch.test.tsx deleted file mode 100644 index caccdb527..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useEntitySelectSearch.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { ChangeEvent } from 'react'; -import { act, renderHook } from '@testing-library/react'; -import { RecoilRoot, useRecoilValue } from 'recoil'; - -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; -import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; - -const scopeId = 'scopeId'; -const Wrapper = ({ children }: { children: React.ReactNode }) => ( - - {children} - -); - -describe('useEntitySelectSearch', () => { - it('should update searchFilter after change event', async () => { - const { result } = renderHook( - () => { - const entitySelectSearchHook = useEntitySelectSearch({ - relationPickerScopeId: 'relation-picker', - }); - const relationPickerScopedStatesHook = useRelationPickerScopedStates({ - relationPickerScopedId: 'relation-picker', - }); - const internallyStoredFilter = useRecoilValue( - relationPickerScopedStatesHook.relationPickerSearchFilterState, - ); - return { entitySelectSearchHook, internallyStoredFilter }; - }, - { - wrapper: Wrapper, - }, - ); - - const filter = 'value'; - - act(() => { - result.current.entitySelectSearchHook.handleSearchFilterChange({ - currentTarget: { value: filter }, - } as ChangeEvent); - }); - - expect(result.current.internallyStoredFilter).toBe(filter); - }); -}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useLimitPerMetadataItem.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useLimitPerMetadataItem.test.tsx index 9bc649377..8596a3343 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useLimitPerMetadataItem.test.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useLimitPerMetadataItem.test.tsx @@ -3,13 +3,13 @@ import { RecoilRoot } from 'recoil'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { useLimitPerMetadataItem } from '@/object-record/relation-picker/hooks/useLimitPerMetadataItem'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; -const scopeId = 'scopeId'; +const instanceId = 'instanceId'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( - + {children} - + ); describe('useLimitPerMetadataItem', () => { diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.test.tsx index 0aeca9fcb..0b085cb56 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.test.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray.test.tsx @@ -3,14 +3,14 @@ import { RecoilRoot, useSetRecoilState } from 'recoil'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray } from '@/object-record/relation-picker/hooks/useMultiObjectRecordsQueryResultFormattedAsObjectRecordForSelectArray'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; -const scopeId = 'scopeId'; +const instanceId = 'instanceId'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( - + {children} - + ); const opportunityId = 'cb702502-4b1d-488e-9461-df3fb096ebf6'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap.test.tsx index 53bd5e2cb..b1a30bbfc 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap.test.tsx +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap.test.tsx @@ -3,14 +3,14 @@ import { RecoilRoot, useSetRecoilState } from 'recoil'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap } from '@/object-record/relation-picker/hooks/useMultiObjectSearchQueryResultFormattedAsObjectRecordsMap'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; -const scopeId = 'scopeId'; +const instanceId = 'instanceId'; const Wrapper = ({ children }: { children: React.ReactNode }) => ( - + {children} - + ); const opportunityId = 'cb702502-4b1d-488e-9461-df3fb096ebf6'; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useRecordSelectSearch.test.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useRecordSelectSearch.test.tsx new file mode 100644 index 000000000..3f521cf18 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/__tests__/useRecordSelectSearch.test.tsx @@ -0,0 +1,45 @@ +import { act, renderHook } from '@testing-library/react'; +import { ChangeEvent } from 'react'; +import { RecoilRoot } from 'recoil'; + +import { useRecordSelectSearch } from '@/object-record/relation-picker/hooks/useRecordSelectSearch'; +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +const instanceId = 'instanceId'; +const Wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + +); + +describe('useRecordSelectSearch', () => { + it('should update searchFilter after change event', async () => { + const { result } = renderHook( + () => { + const recordSelectSearchHook = useRecordSelectSearch({ + recordPickerInstanceId: instanceId, + }); + const internallyStoredFilter = useRecoilComponentValueV2( + recordPickerSearchFilterComponentState, + instanceId, + ); + return { recordSelectSearchHook, internallyStoredFilter }; + }, + { + wrapper: Wrapper, + }, + ); + + const filter = 'value'; + + act(() => { + result.current.recordSelectSearchHook.handleSearchFilterChange({ + currentTarget: { value: filter }, + } as ChangeEvent); + }); + + expect(result.current.internallyStoredFilter).toBe(filter); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates.ts deleted file mode 100644 index e20da80f1..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { getRelationPickerScopedStates } from '@/object-record/relation-picker/utils/getRelationPickerScopedStates'; -import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; - -export const useRelationPickerScopedStates = (args?: { - relationPickerScopedId?: string; -}) => { - const { relationPickerScopedId } = args ?? {}; - - const scopeId = useAvailableComponentInstanceIdOrThrow( - RecordTableComponentInstanceContext, - relationPickerScopedId, - ); - - const { - relationPickerSearchFilterState, - relationPickerPreselectedIdState, - searchQueryState, - } = getRelationPickerScopedStates({ - relationPickerScopeId: scopeId, - }); - - return { - scopeId, - relationPickerSearchFilterState, - relationPickerPreselectedIdState, - searchQueryState, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPicker.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPicker.ts new file mode 100644 index 000000000..723f4e2a0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPicker.ts @@ -0,0 +1,24 @@ +import { recordPickerPreselectedIdComponentState } from '@/object-record/relation-picker/states/recordPickerPreselectedIdComponentState'; +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; + +export const useRecordPicker = ({ + recordPickerInstanceId, +}: { + recordPickerInstanceId?: string; +}) => { + const setRecordPickerSearchFilter = useSetRecoilComponentStateV2( + recordPickerSearchFilterComponentState, + recordPickerInstanceId, + ); + + const setRecordPickerPreselectedId = useSetRecoilComponentStateV2( + recordPickerPreselectedIdComponentState, + recordPickerInstanceId, + ); + + return { + setRecordPickerSearchFilter, + setRecordPickerPreselectedId, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPickerRecordsOptions.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPickerRecordsOptions.ts new file mode 100644 index 000000000..9064a67ad --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordPickerRecordsOptions.ts @@ -0,0 +1,26 @@ +import { recordPickerSearchFilterComponentState } from '@/object-record/relation-picker/states/recordPickerSearchFilterComponentState'; +import { useFilteredSearchRecordQuery } from '@/search/hooks/useFilteredSearchRecordQuery'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +export const useRecordPickerRecordsOptions = ({ + objectNameSingular, + selectedRecordIds = [], + excludedRecordIds = [], +}: { + objectNameSingular: string; + selectedRecordIds?: string[]; + excludedRecordIds?: string[]; +}) => { + const recordPickerSearchFilter = useRecoilComponentValueV2( + recordPickerSearchFilterComponentState, + ); + + const records = useFilteredSearchRecordQuery({ + searchFilter: recordPickerSearchFilter, + selectedIds: selectedRecordIds, + excludedRecordIds: excludedRecordIds, + objectNameSingular, + }); + + return { records, recordPickerSearchFilter }; +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordSelectSearch.ts similarity index 52% rename from packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts rename to packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordSelectSearch.ts index d696b235a..1e615cb1f 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useEntitySelectSearch.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRecordSelectSearch.ts @@ -1,17 +1,17 @@ import { useDebouncedCallback } from 'use-debounce'; -import { useRelationPicker } from '@/object-record/relation-picker/hooks/useRelationPicker'; +import { useRecordPicker } from '@/object-record/relation-picker/hooks/useRecordPicker'; -export const useEntitySelectSearch = ({ - relationPickerScopeId, +export const useRecordSelectSearch = ({ + recordPickerInstanceId, }: { - relationPickerScopeId?: string; + recordPickerInstanceId?: string; } = {}) => { - const { setRelationPickerSearchFilter, setRelationPickerPreselectedId } = - useRelationPicker({ relationPickerScopeId }); + const { setRecordPickerSearchFilter, setRecordPickerPreselectedId } = + useRecordPicker({ recordPickerInstanceId }); const debouncedSetSearchFilter = useDebouncedCallback( - setRelationPickerSearchFilter, + setRecordPickerSearchFilter, 100, { leading: true, @@ -20,14 +20,14 @@ export const useEntitySelectSearch = ({ const resetSearchFilter = () => { debouncedSetSearchFilter(''); - setRelationPickerPreselectedId(''); + setRecordPickerPreselectedId(''); }; const handleSearchFilterChange = ( event: React.ChangeEvent, ) => { debouncedSetSearchFilter(event.currentTarget.value); - setRelationPickerPreselectedId(''); + setRecordPickerPreselectedId(''); }; return { diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPicker.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPicker.ts deleted file mode 100644 index 50c00b150..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPicker.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { useSetRecoilState } from 'recoil'; - -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; - -type useRelationPickeProps = { - relationPickerScopeId?: string; -}; - -export const useRelationPicker = (props?: useRelationPickeProps) => { - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, - props?.relationPickerScopeId, - ); - - const { relationPickerSearchFilterState, relationPickerPreselectedIdState } = - useRelationPickerScopedStates({ - relationPickerScopedId: scopeId, - }); - - const setRelationPickerSearchFilter = useSetRecoilState( - relationPickerSearchFilterState, - ); - - const setRelationPickerPreselectedId = useSetRecoilState( - relationPickerPreselectedIdState, - ); - - return { - setRelationPickerSearchFilter, - setRelationPickerPreselectedId, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions.ts b/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions.ts deleted file mode 100644 index f4234900a..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/hooks/useRelationPickerEntitiesOptions.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useRecoilValue } from 'recoil'; - -import { useRelationPickerScopedStates } from '@/object-record/relation-picker/hooks/internal/useRelationPickerScopedStates'; -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; -import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; - -export const useRelationPickerEntitiesOptions = ({ - relationObjectNameSingular, - selectedRelationRecordIds = [], - excludedRelationRecordIds = [], -}: { - relationObjectNameSingular: string; - selectedRelationRecordIds?: string[]; - excludedRelationRecordIds?: string[]; -}) => { - const scopeId = useAvailableScopeIdOrThrow( - RelationPickerScopeInternalContext, - ); - - const { relationPickerSearchFilterState } = useRelationPickerScopedStates({ - relationPickerScopedId: scopeId, - }); - const relationPickerSearchFilter = useRecoilValue( - relationPickerSearchFilterState, - ); - - const entities = useFilteredSearchEntityQuery({ - searchFilter: relationPickerSearchFilter, - selectedIds: selectedRelationRecordIds, - excludeRecordIds: excludedRelationRecordIds, - objectNameSingular: relationObjectNameSingular, - }); - - return { entities, relationPickerSearchFilter }; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/RelationPickerScope.tsx b/packages/twenty-front/src/modules/object-record/relation-picker/scopes/RelationPickerScope.tsx deleted file mode 100644 index be06e2796..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/RelationPickerScope.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { ReactNode } from 'react'; - -import { RelationPickerScopeInternalContext } from '@/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext'; - -type RelationPickerScopeProps = { - children: ReactNode; - relationPickerScopeId: string; -}; - -export const RelationPickerScope = ({ - children, - relationPickerScopeId, -}: RelationPickerScopeProps) => { - return ( - - {children} - - ); -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts b/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts deleted file mode 100644 index eda26d3f1..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/scopes/scope-internal-context/RelationPickerScopeInternalContext.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext'; -import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey'; - -type RelationPickerScopeInternalContextProps = RecoilComponentStateKey; - -export const RelationPickerScopeInternalContext = - createScopeInternalContext(); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext.ts new file mode 100644 index 000000000..919d639c0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext.ts @@ -0,0 +1,4 @@ +import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext'; + +export const RecordPickerComponentInstanceContext = + createComponentInstanceContext(); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/recoil-scope-contexts/RelationPickerRecoilScopeContext.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/recoil-scope-contexts/RelationPickerRecoilScopeContext.ts deleted file mode 100644 index 25dbee7f3..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/states/recoil-scope-contexts/RelationPickerRecoilScopeContext.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createContext } from 'react'; - -export const RelationPickerRecoilScopeContext = createContext( - 'relation-picker-context', -); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerPreselectedIdComponentState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerPreselectedIdComponentState.ts new file mode 100644 index 000000000..f03247a20 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerPreselectedIdComponentState.ts @@ -0,0 +1,10 @@ +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const recordPickerPreselectedIdComponentState = createComponentStateV2< + string | undefined +>({ + key: 'recordPickerPreselectedIdComponentState', + defaultValue: undefined, + componentInstanceContext: RecordPickerComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerSearchFilterComponentState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerSearchFilterComponentState.ts new file mode 100644 index 000000000..b0e5efc40 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/states/recordPickerSearchFilterComponentState.ts @@ -0,0 +1,9 @@ +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const recordPickerSearchFilterComponentState = + createComponentStateV2({ + key: 'recordPickerSearchFilterComponentState', + defaultValue: '', + componentInstanceContext: RecordPickerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerPreselectedIdScopedState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerPreselectedIdScopedState.ts deleted file mode 100644 index c0c2ba232..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerPreselectedIdScopedState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const relationPickerPreselectedIdScopedState = createComponentState< - string | undefined ->({ - key: 'relationPickerPreselectedIdScopedState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerSearchFilterScopedState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerSearchFilterScopedState.ts deleted file mode 100644 index 608415575..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/states/relationPickerSearchFilterScopedState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const relationPickerSearchFilterScopedState = - createComponentState({ - key: 'relationPickerSearchFilterScopedState', - defaultValue: '', - }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryComponentState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryComponentState.ts new file mode 100644 index 000000000..f03a4cd22 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryComponentState.ts @@ -0,0 +1,10 @@ +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; +import { SearchQuery } from '@/object-record/relation-picker/types/SearchQuery'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const searchQueryComponentState = + createComponentStateV2({ + key: 'searchQueryComponentState', + defaultValue: null, + componentInstanceContext: RecordPickerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryScopedState.ts b/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryScopedState.ts deleted file mode 100644 index 70a072e0f..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/states/searchQueryScopedState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { SearchQuery } from '@/object-record/relation-picker/types/SearchQuery'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const searchQueryScopedState = createComponentState({ - key: 'searchQueryScopedState', - defaultValue: null, -}); diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/types/EntitiesForMultipleEntitySelect.ts b/packages/twenty-front/src/modules/object-record/relation-picker/types/EntitiesForMultipleEntitySelect.ts deleted file mode 100644 index fe74c29d4..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/types/EntitiesForMultipleEntitySelect.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; - -export type EntitiesForMultipleEntitySelect< - CustomEntityForSelect extends EntityForSelect, -> = { - selectedEntities: CustomEntityForSelect[]; - filteredSelectedEntities: CustomEntityForSelect[]; - entitiesToSelect: CustomEntityForSelect[]; - loading: boolean; -}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/types/EntityForSelect.ts b/packages/twenty-front/src/modules/object-record/relation-picker/types/RecordForSelect.ts similarity index 76% rename from packages/twenty-front/src/modules/object-record/relation-picker/types/EntityForSelect.ts rename to packages/twenty-front/src/modules/object-record/relation-picker/types/RecordForSelect.ts index d7958ba24..a2cd90af7 100644 --- a/packages/twenty-front/src/modules/object-record/relation-picker/types/EntityForSelect.ts +++ b/packages/twenty-front/src/modules/object-record/relation-picker/types/RecordForSelect.ts @@ -1,6 +1,6 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecordIdentifier } from '@/object-record/types/ObjectRecordIdentifier'; -export type EntityForSelect = ObjectRecordIdentifier & { +export type RecordForSelect = ObjectRecordIdentifier & { record: ObjectRecord; }; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/types/RecordsForMultipleRecordSelect.ts b/packages/twenty-front/src/modules/object-record/relation-picker/types/RecordsForMultipleRecordSelect.ts new file mode 100644 index 000000000..63cbc38c1 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/relation-picker/types/RecordsForMultipleRecordSelect.ts @@ -0,0 +1,10 @@ +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; + +export type RecordsForMultipleRecordSelect< + CustomRecordForSelect extends RecordForSelect, +> = { + selectedRecords: CustomRecordForSelect[]; + filteredSelectedRecords: CustomRecordForSelect[]; + recordsToSelect: CustomRecordForSelect[]; + loading: boolean; +}; diff --git a/packages/twenty-front/src/modules/object-record/relation-picker/utils/getRelationPickerScopedStates.ts b/packages/twenty-front/src/modules/object-record/relation-picker/utils/getRelationPickerScopedStates.ts deleted file mode 100644 index 52e171ba9..000000000 --- a/packages/twenty-front/src/modules/object-record/relation-picker/utils/getRelationPickerScopedStates.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { relationPickerPreselectedIdScopedState } from '@/object-record/relation-picker/states/relationPickerPreselectedIdScopedState'; -import { relationPickerSearchFilterScopedState } from '@/object-record/relation-picker/states/relationPickerSearchFilterScopedState'; -import { searchQueryScopedState } from '@/object-record/relation-picker/states/searchQueryScopedState'; -import { getScopedStateDeprecated } from '@/ui/utilities/recoil-scope/utils/getScopedStateDeprecated'; - -export const getRelationPickerScopedStates = ({ - relationPickerScopeId, -}: { - relationPickerScopeId: string; -}) => { - const searchQueryState = getScopedStateDeprecated( - searchQueryScopedState, - relationPickerScopeId, - ); - - const relationPickerPreselectedIdState = getScopedStateDeprecated( - relationPickerPreselectedIdScopedState, - relationPickerScopeId, - ); - - const relationPickerSearchFilterState = getScopedStateDeprecated( - relationPickerSearchFilterScopedState, - relationPickerScopeId, - ); - - return { - relationPickerSearchFilterState, - relationPickerPreselectedIdState, - searchQueryState, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts index 73bacf2e7..3abb7bcef 100644 --- a/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts +++ b/packages/twenty-front/src/modules/object-record/select/hooks/useRecordsForSelect.ts @@ -16,14 +16,14 @@ export const useRecordsForSelect = ({ sortOrder = 'AscNullsLast', selectedIds, limit, - excludeRecordIds = [], + excludedRecordIds = [], objectNameSingular, }: { searchFilterText: string; sortOrder?: OrderBy; selectedIds: string[]; limit?: number; - excludeRecordIds?: string[]; + excludedRecordIds?: string[]; objectNameSingular: string; }) => { const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({ @@ -91,7 +91,7 @@ export const useRecordsForSelect = ({ skip: !selectedIds.length, }); - const notFilterIds = [...selectedIds, ...excludeRecordIds]; + const notFilterIds = [...selectedIds, ...excludedRecordIds]; const notFilter = notFilterIds.length ? { not: { id: { in: notFilterIds } } } : undefined; diff --git a/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts b/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts index 1ee8e850a..46f145c45 100644 --- a/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts +++ b/packages/twenty-front/src/modules/object-record/utils/getRecordChipGenerators.ts @@ -47,13 +47,13 @@ export const getRecordChipGenerators = ( labelIdentifierFieldMetadataItem?.id === fieldMetadataItem.id; const currentObjectNameSingular = objectMetadataItem.nameSingular; - const fieldRelationObjectNameSingular = + const fieldObjectNameSingular = fieldMetadataItem.relationDefinition?.targetObjectMetadata .nameSingular ?? undefined; const objectNameSingularToFind = isLabelIdentifier ? currentObjectNameSingular - : fieldRelationObjectNameSingular; + : fieldObjectNameSingular; const objectMetadataItemToUse = objectMetadataItems.find( (objectMetadataItem) => diff --git a/packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchEntityQuery.ts b/packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchRecordQuery.ts similarity index 98% rename from packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchEntityQuery.ts rename to packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchRecordQuery.ts index 6f25feba8..b57f7ba7a 100644 --- a/packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchEntityQuery.ts +++ b/packages/twenty-front/src/modules/search/hooks/__mocks__/useFilteredSearchRecordQuery.ts @@ -144,7 +144,7 @@ export const query = gql` `; export const variables = { - entitiesToSelect: { + recordsToSelect: { limit: 10, filter: { and: [ @@ -154,7 +154,7 @@ export const variables = { }, orderBy: [{ name: 'AscNullsLast' }], }, - filteredSelectedEntities: { + filteredSelectedRecords: { limit: 60, filter: { and: [ diff --git a/packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchEntityQuery.test.tsx b/packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchRecordQuery.test.tsx similarity index 77% rename from packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchEntityQuery.test.tsx rename to packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchRecordQuery.test.tsx index 6b2409af4..05ede72d5 100644 --- a/packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchEntityQuery.test.tsx +++ b/packages/twenty-front/src/modules/search/hooks/__tests__/useFilteredSearchRecordQuery.test.tsx @@ -5,7 +5,7 @@ import { RecoilRoot, useSetRecoilState } from 'recoil'; import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; -import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect'; +import { RecordsForMultipleRecordSelect } from '@/object-record/relation-picker/types/RecordsForMultipleRecordSelect'; import { SnackBarProviderScope } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarProviderScope'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; @@ -13,14 +13,14 @@ import { query, responseData, variables, -} from '../__mocks__/useFilteredSearchEntityQuery'; -import { useFilteredSearchEntityQuery } from '../useFilteredSearchEntityQuery'; +} from '../__mocks__/useFilteredSearchRecordQuery'; +import { useFilteredSearchRecordQuery } from '../useFilteredSearchRecordQuery'; const mocks = [ { request: { query, - variables: variables.entitiesToSelect, + variables: variables.recordsToSelect, }, result: jest.fn(() => ({ data: { @@ -31,7 +31,7 @@ const mocks = [ { request: { query, - variables: variables.filteredSelectedEntities, + variables: variables.filteredSelectedRecords, }, result: jest.fn(() => ({ data: { @@ -62,7 +62,7 @@ const Wrapper = ({ children }: { children: ReactNode }) => ( ); -describe('useFilteredSearchEntityQuery', () => { +describe('useFilteredSearchRecordQuery', () => { it('returns the correct result when everything is provided', async () => { const { result } = renderHook( () => { @@ -79,10 +79,10 @@ describe('useFilteredSearchEntityQuery', () => { setMetadataItems(generatedMockObjectMetadataItems); - return useFilteredSearchEntityQuery({ + return useFilteredSearchRecordQuery({ selectedIds: ['1'], limit: 10, - excludeRecordIds: ['2'], + excludedRecordIds: ['2'], objectNameSingular: 'person', searchFilter: 'Entity', }); @@ -90,10 +90,10 @@ describe('useFilteredSearchEntityQuery', () => { { wrapper: Wrapper }, ); - const expectedResult: EntitiesForMultipleEntitySelect = { - selectedEntities: [], - filteredSelectedEntities: [], - entitiesToSelect: [], + const expectedResult: RecordsForMultipleRecordSelect = { + selectedRecords: [], + filteredSelectedRecords: [], + recordsToSelect: [], loading: true, }; diff --git a/packages/twenty-front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts b/packages/twenty-front/src/modules/search/hooks/useFilteredSearchRecordQuery.ts similarity index 73% rename from packages/twenty-front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts rename to packages/twenty-front/src/modules/search/hooks/useFilteredSearchRecordQuery.ts index 9d6973385..7d49da6c2 100644 --- a/packages/twenty-front/src/modules/search/hooks/useFilteredSearchEntityQuery.ts +++ b/packages/twenty-front/src/modules/search/hooks/useFilteredSearchRecordQuery.ts @@ -1,27 +1,27 @@ import { useMapToObjectRecordIdentifier } from '@/object-metadata/hooks/useMapToObjectRecordIdentifier'; import { DEFAULT_SEARCH_REQUEST_LIMIT } from '@/object-record/constants/DefaultSearchRequestLimit'; import { useSearchRecords } from '@/object-record/hooks/useSearchRecords'; -import { EntitiesForMultipleEntitySelect } from '@/object-record/relation-picker/types/EntitiesForMultipleEntitySelect'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect'; +import { RecordsForMultipleRecordSelect } from '@/object-record/relation-picker/types/RecordsForMultipleRecordSelect'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { isDefined } from '~/utils/isDefined'; -// TODO: use this for all search queries, because we need selectedEntities and entitiesToSelect each time we want to search +// TODO: use this for all search queries, because we need selectedRecords and recordsToSelect each time we want to search // Filtered entities to select are -export const useFilteredSearchEntityQuery = ({ +export const useFilteredSearchRecordQuery = ({ selectedIds, limit, - excludeRecordIds = [], + excludedRecordIds = [], objectNameSingular, searchFilter, }: { selectedIds: string[]; limit?: number; - excludeRecordIds?: string[]; + excludedRecordIds?: string[]; objectNameSingular: string; searchFilter?: string; -}): EntitiesForMultipleEntitySelect => { +}): RecordsForMultipleRecordSelect => { const { mapToObjectRecordIdentifier } = useMapToObjectRecordIdentifier({ objectNameSingular, }); @@ -50,7 +50,7 @@ export const useFilteredSearchEntityQuery = ({ searchInput: searchFilter, }); - const notFilterIds = [...selectedIds, ...excludeRecordIds]; + const notFilterIds = [...selectedIds, ...excludedRecordIds]; const notFilter = notFilterIds.length ? { not: { id: { in: notFilterIds } } } : undefined; @@ -63,11 +63,11 @@ export const useFilteredSearchEntityQuery = ({ }); return { - selectedEntities: selectedRecords.map(mappingFunction).filter(isDefined), - filteredSelectedEntities: filteredSelectedRecords + selectedRecords: selectedRecords.map(mappingFunction).filter(isDefined), + filteredSelectedRecords: filteredSelectedRecords .map(mappingFunction) .filter(isDefined), - entitiesToSelect: recordsToSelect.map(mappingFunction).filter(isDefined), + recordsToSelect: recordsToSelect.map(mappingFunction).filter(isDefined), loading: recordsToSelectLoading || filteredSelectedRecordsLoading || diff --git a/packages/twenty-front/src/testing/decorators/RecordPickerDecorator.tsx b/packages/twenty-front/src/testing/decorators/RecordPickerDecorator.tsx new file mode 100644 index 000000000..46fd5bbc2 --- /dev/null +++ b/packages/twenty-front/src/testing/decorators/RecordPickerDecorator.tsx @@ -0,0 +1,11 @@ +import { Decorator } from '@storybook/react'; + +import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext'; + +export const RecordPickerDecorator: Decorator = (Story) => ( + + + +); diff --git a/packages/twenty-front/src/testing/decorators/RelationPickerDecorator.tsx b/packages/twenty-front/src/testing/decorators/RelationPickerDecorator.tsx deleted file mode 100644 index cff85cae8..000000000 --- a/packages/twenty-front/src/testing/decorators/RelationPickerDecorator.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Decorator } from '@storybook/react'; - -import { RelationPickerScope } from '@/object-record/relation-picker/scopes/RelationPickerScope'; - -export const RelationPickerDecorator: Decorator = (Story) => ( - - - -);