diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx index 5e6810cfb..35161f2fe 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownHiddenRecordGroupsContent.tsx @@ -10,46 +10,50 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { const { currentContentId, - viewType, recordIndexId, objectMetadataItem, onContentChange, closeDropdown, } = useOptionsDropdown(); - const { objectNamePlural } = useObjectNamePluralFromSingular({ - objectNameSingular: objectMetadataItem.nameSingular, - }); + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + ); - const { hiddenRecordGroups, viewGroupFieldMetadataItem } = useRecordGroups({ + const hiddenRecordGroupIds = useRecoilComponentValueV2( + hiddenRecordGroupIdsComponentSelector, + ); + + const { objectNamePlural } = useObjectNamePluralFromSingular({ objectNameSingular: objectMetadataItem.nameSingular, }); const { handleVisibilityChange: handleRecordGroupVisibilityChange } = useRecordGroupVisibility({ viewBarId: recordIndexId, - viewType, }); const viewGroupSettingsUrl = getSettingsPagePath( SettingsPath.ObjectFieldEdit, { objectSlug: objectNamePlural, - fieldSlug: viewGroupFieldMetadataItem?.name ?? '', + fieldSlug: recordGroupFieldMetadata?.name ?? '', }, ); @@ -61,11 +65,11 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && - hiddenRecordGroups.length === 0 + hiddenRecordGroupIds.length === 0 ) { onContentChange('recordGroups'); } - }, [hiddenRecordGroups, currentContentId, onContentChange]); + }, [hiddenRecordGroupIds, currentContentId, onContentChange]); return ( <> @@ -74,13 +78,13 @@ export const ObjectOptionsDropdownHiddenRecordGroupsContent = () => { StartIcon={IconChevronLeft} onClick={() => onContentChange('recordGroups')} > - Hidden {viewGroupFieldMetadataItem?.label} + Hidden {recordGroupFieldMetadata?.label} { objectNameSingular: objectMetadataItem.nameSingular, }); + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + ); + useScopedHotkeys( [Key.Escape], () => { @@ -64,10 +69,6 @@ export const ObjectOptionsDropdownMenuContent = () => { viewBarId: recordIndexId, }); - const { viewGroupFieldMetadataItem } = useRecordGroups({ - objectNameSingular: objectMetadataItem.nameSingular, - }); - const { openObjectRecordsSpreasheetImportDialog } = useOpenObjectRecordsSpreadsheetImportDialog( objectMetadataItem.nameSingular, @@ -113,7 +114,7 @@ export const ObjectOptionsDropdownMenuContent = () => { onClick={() => onContentChange('recordGroups')} LeftIcon={IconLayoutList} text="Group by" - contextualText={viewGroupFieldMetadataItem?.label} + contextualText={recordGroupFieldMetadata?.label} hasSubMenu /> )} diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx index bb8660242..238442058 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupFieldsContent.tsx @@ -12,7 +12,7 @@ import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObje import { StyledInput } from '@/object-record/object-filter-dropdown/components/ObjectFilterDropdownFilterSelect'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { useSearchRecordGroupField } from '@/object-record/object-options-dropdown/hooks/useSearchRecordGroupField'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; +import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { useHandleRecordGroupField } from '@/object-record/record-index/hooks/useHandleRecordGroupField'; import { getSettingsPagePath } from '@/settings/utils/getSettingsPagePath'; import { SettingsPath } from '@/types/SettingsPath'; @@ -20,6 +20,7 @@ import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenu import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; @@ -38,9 +39,9 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { objectNameSingular: objectMetadataItem.nameSingular, }); - const { hiddenRecordGroups } = useRecordGroups({ - objectNameSingular: objectMetadataItem.nameSingular, - }); + const hiddenRecordGroupIds = useRecoilComponentValueV2( + hiddenRecordGroupIdsComponentSelector, + ); const { recordGroupFieldSearchInput, @@ -68,11 +69,11 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && - hiddenRecordGroups.length === 0 + hiddenRecordGroupIds.length === 0 ) { onContentChange('recordGroups'); } - }, [hiddenRecordGroups, currentContentId, onContentChange]); + }, [hiddenRecordGroupIds, currentContentId, onContentChange]); return ( <> diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx index fcfeea029..b2a8c623d 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupSortContent.tsx @@ -8,24 +8,21 @@ import { } from 'twenty-ui'; import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; +import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; export const ObjectOptionsDropdownRecordGroupSortContent = () => { - const { - currentContentId, - objectMetadataItem, - onContentChange, - closeDropdown, - } = useOptionsDropdown(); + const { currentContentId, onContentChange, closeDropdown } = + useOptionsDropdown(); - const { hiddenRecordGroups } = useRecordGroups({ - objectNameSingular: objectMetadataItem.nameSingular, - }); + const hiddenRecordGroupIds = useRecoilComponentValueV2( + hiddenRecordGroupIdsComponentSelector, + ); const setRecordGroupSort = useSetRecoilComponentStateV2( recordIndexRecordGroupSortComponentState, @@ -39,11 +36,11 @@ export const ObjectOptionsDropdownRecordGroupSortContent = () => { useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && - hiddenRecordGroups.length === 0 + hiddenRecordGroupIds.length === 0 ) { onContentChange('recordGroups'); } - }, [hiddenRecordGroups, currentContentId, onContentChange]); + }, [hiddenRecordGroupIds, currentContentId, onContentChange]); return ( <> diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx index 94db1268f..19a892be1 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownRecordGroupsContent.tsx @@ -13,8 +13,10 @@ import { import { useOptionsDropdown } from '@/object-record/object-options-dropdown/hooks/useOptionsDropdown'; import { RecordGroupsVisibilityDropdownSection } from '@/object-record/record-group/components/RecordGroupsVisibilityDropdownSection'; import { useRecordGroupReorder } from '@/object-record/record-group/hooks/useRecordGroupReorder'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { hiddenRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; import { recordIndexRecordGroupIsDraggableSortComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader'; @@ -26,22 +28,20 @@ import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; export const ObjectOptionsDropdownRecordGroupsContent = () => { const isViewGroupEnabled = useIsFeatureEnabled('IS_VIEW_GROUPS_ENABLED'); - const { - currentContentId, - viewType, - recordIndexId, - objectMetadataItem, - onContentChange, - resetContent, - } = useOptionsDropdown(); + const { currentContentId, recordIndexId, onContentChange, resetContent } = + useOptionsDropdown(); - const { - hiddenRecordGroups, - visibleRecordGroups, - viewGroupFieldMetadataItem, - } = useRecordGroups({ - objectNameSingular: objectMetadataItem.nameSingular, - }); + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + ); + + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); + + const hiddenRecordGroupIds = useRecoilComponentValueV2( + hiddenRecordGroupIdsComponentSelector, + ); const isDragableSortRecordGroup = useRecoilComponentValueV2( recordIndexRecordGroupIsDraggableSortComponentSelector, @@ -56,23 +56,21 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { handleHideEmptyRecordGroupChange, } = useRecordGroupVisibility({ viewBarId: recordIndexId, - viewType, }); const { handleOrderChange: handleRecordGroupOrderChange } = useRecordGroupReorder({ - objectNameSingular: objectMetadataItem.nameSingular, viewBarId: recordIndexId, }); useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && - hiddenRecordGroups.length === 0 + hiddenRecordGroupIds.length === 0 ) { onContentChange('recordGroups'); } - }, [hiddenRecordGroups, currentContentId, onContentChange]); + }, [hiddenRecordGroupIds, currentContentId, onContentChange]); return ( <> @@ -86,9 +84,9 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { onClick={() => onContentChange('recordGroupFields')} LeftIcon={IconLayoutList} text={ - !viewGroupFieldMetadataItem + !recordGroupFieldMetadata ? 'Group by' - : `Group by "${viewGroupFieldMetadataItem.label}"` + : `Group by "${recordGroupFieldMetadata.label}"` } hasSubMenu /> @@ -108,12 +106,12 @@ export const ObjectOptionsDropdownRecordGroupsContent = () => { toggleSize="small" /> - {visibleRecordGroups.length > 0 && ( + {visibleRecordGroupIds.length > 0 && ( <> { /> )} - {hiddenRecordGroups.length > 0 && ( + {hiddenRecordGroupIds.length > 0 && ( <> onContentChange('hiddenRecordGroups')} LeftIcon={IconEyeOff} - text={`Hidden ${viewGroupFieldMetadataItem?.label ?? ''}`} + text={`Hidden ${recordGroupFieldMetadata?.label ?? ''}`} /> diff --git a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard.ts b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard.ts index ccb19275b..3dba54dc3 100644 --- a/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard.ts +++ b/packages/twenty-front/src/modules/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard.ts @@ -4,10 +4,11 @@ import { useRecoilState } from 'recoil'; import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useSaveCurrentViewFields } from '@/views/hooks/useSaveCurrentViewFields'; import { useUpdateCurrentView } from '@/views/hooks/useUpdateCurrentView'; import { GraphQLView } from '@/views/types/GraphQLView'; @@ -32,11 +33,12 @@ export const useObjectOptionsForBoard = ({ const { saveViewFields } = useSaveCurrentViewFields(viewBarId); const { updateCurrentView } = useUpdateCurrentView(viewBarId); - const { isCompactModeActiveState } = useRecordBoard(recordBoardId); - const [isCompactModeActive, setIsCompactModeActive] = useRecoilState( - isCompactModeActiveState, - ); + const [isCompactModeActive, setIsCompactModeActive] = + useRecoilComponentStateV2( + isRecordBoardCompactModeActiveComponentState, + recordBoardId, + ); const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx index 4c5076d9f..a28c71b8e 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoard.tsx @@ -1,7 +1,7 @@ import styled from '@emotion/styled'; import { DragDropContext, OnDragEndResponder } from '@hello-pangea/dnd'; // Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350 import { useContext, useRef } from 'react'; -import { useRecoilCallback, useRecoilValue } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { Key } from 'ts-key-enum'; import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; @@ -9,11 +9,15 @@ import { RecordBoardHeader } from '@/object-record/record-board/components/Recor import { RecordBoardStickyHeaderEffect } from '@/object-record/record-board/components/RecordBoardStickyHeaderEffect'; import { RECORD_BOARD_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-board/constants/RecordBoardClickOutsideListenerId'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useRecordBoardSelection } from '@/object-record/record-board/hooks/useRecordBoardSelection'; import { RecordBoardColumn } from '@/object-record/record-board/record-board-column/components/RecordBoardColumn'; import { RecordBoardScope } from '@/object-record/record-board/scopes/RecordBoardScope'; +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { getDraggedRecordPosition } from '@/object-record/record-board/utils/getDraggedRecordPosition'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; @@ -21,6 +25,9 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { useListenClickOutsideV2 } from '@/ui/utilities/pointer-event/hooks/useListenClickOutsideV2'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useScrollRestoration } from '~/hooks/useScrollRestoration'; const StyledContainer = styled.div` @@ -58,14 +65,17 @@ export const RecordBoard = () => { useContext(RecordBoardContext); const boardRef = useRef(null); - const { - columnIdsState, - columnsFamilySelector, - recordIdsByColumnIdFamilyState, - allRecordIdsSelector, - } = useRecordBoardStates(recordBoardId); + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); - const columnIds = useRecoilValue(columnIdsState); + const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( + recordIndexRowIdsByGroupComponentFamilyState, + ); + + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, + ); const { resetRecordSelection, setRecordAsSelected } = useRecordBoardSelection(recordBoardId); @@ -85,15 +95,16 @@ export const RecordBoard = () => { const selectAll = useRecoilCallback( ({ snapshot }) => () => { - const allRecordIds = snapshot - .getLoadable(allRecordIdsSelector()) - .getValue(); + const allRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRowIdsState, + ); for (const recordId of allRecordIds) { setRecordAsSelected(recordId, true); } }, - [allRecordIdsSelector, setRecordAsSelected], + [recordIndexAllRowIdsState, setRecordAsSelected], ); useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table); @@ -111,42 +122,40 @@ export const RecordBoard = () => { if (!result.destination) return; const draggedRecordId = result.draggableId; - const sourceColumnId = result.source.droppableId; - const destinationColumnId = result.destination.droppableId; + const sourceRecordGroupId = result.source.droppableId; + const destinationRecordGroupId = result.destination.droppableId; const destinationIndexInColumn = result.destination.index; - if (!destinationColumnId || !selectFieldMetadataItem) return; + if (!destinationRecordGroupId || !selectFieldMetadataItem) return; - const column = snapshot - .getLoadable(columnsFamilySelector(destinationColumnId)) - .getValue(); + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(destinationRecordGroupId), + ); - if (!column) return; + if (!recordGroup) return; - const destinationColumnRecordIds = snapshot - .getLoadable(recordIdsByColumnIdFamilyState(destinationColumnId)) - .getValue(); - const otherRecordsInDestinationColumn = - sourceColumnId === destinationColumnId - ? destinationColumnRecordIds.filter( + const destinationRecordByGroupIds = getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(destinationRecordGroupId), + ); + const otherRecordIdsInDestinationColumn = + sourceRecordGroupId === destinationRecordGroupId + ? destinationRecordByGroupIds.filter( (recordId) => recordId !== draggedRecordId, ) - : destinationColumnRecordIds; + : destinationRecordByGroupIds; const recordBeforeId = - otherRecordsInDestinationColumn[destinationIndexInColumn - 1]; + otherRecordIdsInDestinationColumn[destinationIndexInColumn - 1]; const recordBefore = recordBeforeId - ? snapshot - .getLoadable(recordStoreFamilyState(recordBeforeId)) - .getValue() + ? getSnapshotValue(snapshot, recordStoreFamilyState(recordBeforeId)) : null; const recordAfterId = - otherRecordsInDestinationColumn[destinationIndexInColumn]; + otherRecordIdsInDestinationColumn[destinationIndexInColumn]; const recordAfter = recordAfterId - ? snapshot - .getLoadable(recordStoreFamilyState(recordAfterId)) - .getValue() + ? getSnapshotValue(snapshot, recordStoreFamilyState(recordAfterId)) : null; const draggedRecordPosition = getDraggedRecordPosition( @@ -157,14 +166,13 @@ export const RecordBoard = () => { updateOneRecord({ idToUpdate: draggedRecordId, updateOneRecordInput: { - [selectFieldMetadataItem.name]: column.value, + [selectFieldMetadataItem.name]: recordGroup.value, position: draggedRecordPosition, }, }); }, [ - columnsFamilySelector, - recordIdsByColumnIdFamilyState, + recordIndexRowIdsByGroupFamilyState, selectFieldMetadataItem, updateOneRecord, ], @@ -182,32 +190,36 @@ export const RecordBoard = () => { onColumnsChange={() => {}} onFieldsChange={() => {}} > - - - - - - - - - {columnIds.map((columnId) => ( - - ))} - - - - - - - - + + + + + + + + + + {visibleRecordGroupIds.map((recordGroupId) => ( + + ))} + + + + + + + + + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx index b28453238..e62c65811 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/components/RecordBoardHeader.tsx @@ -1,7 +1,6 @@ -import { useRecoilValue } from 'recoil'; - -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumnHeaderWrapper } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; const StyledHeaderContainer = styled.div` @@ -24,14 +23,17 @@ const StyledHeaderContainer = styled.div` `; export const RecordBoardHeader = () => { - const { columnIdsState } = useRecordBoardStates(); - - const columnIds = useRecoilValue(columnIdsState); + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); return ( - {columnIds.map((columnId) => ( - + {visibleRecordGroupIds.map((recordGroupId) => ( + ))} ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts deleted file mode 100644 index a26506254..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext'; -import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; -import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; -import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState'; -import { isRecordBoardFetchingRecordsComponentState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState'; -import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState'; -import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; -import { recordBoardFiltersComponentState } from '@/object-record/record-board/states/recordBoardFiltersComponentState'; -import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState'; -import { recordBoardObjectSingularNameComponentState } from '@/object-record/record-board/states/recordBoardObjectSingularNameComponentState'; -import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; -import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; -import { recordBoardSortsComponentState } from '@/object-record/record-board/states/recordBoardSortsComponentState'; -import { recordBoardAllRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector'; -import { recordBoardColumnsComponentFamilySelector } from '@/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector'; -import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; -import { recordBoardShouldFetchMoreComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector'; -import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; -import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; -import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId'; -import { extractComponentFamilyState } from '@/ui/utilities/state/component-state/utils/extractComponentFamilyState'; -import { extractComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/extractComponentReadOnlySelector'; -import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; - -export const useRecordBoardStates = (recordBoardId?: string) => { - const scopeId = useAvailableScopeIdOrThrow( - RecordBoardScopeInternalContext, - getScopeIdOrUndefinedFromComponentId(recordBoardId), - ); - - return { - scopeId, - objectSingularNameState: extractComponentState( - recordBoardObjectSingularNameComponentState, - scopeId, - ), - kanbanFieldMetadataNameState: extractComponentState( - recordBoardKanbanFieldMetadataNameComponentState, - scopeId, - ), - isFetchingRecordState: extractComponentState( - isRecordBoardFetchingRecordsComponentState, - scopeId, - ), - isFetchingRecordsByColumnState: extractComponentFamilyState( - isRecordBoardFetchingRecordsByColumnFamilyState, - scopeId, - ), - columnIdsState: extractComponentState( - recordBoardColumnIdsComponentState, - scopeId, - ), - columnsFamilySelector: extractComponentFamilyState( - recordBoardColumnsComponentFamilySelector, - scopeId, - ), - - filtersState: extractComponentState( - recordBoardFiltersComponentState, - scopeId, - ), - sortsState: extractComponentState(recordBoardSortsComponentState, scopeId), - fieldDefinitionsState: extractComponentState( - recordBoardFieldDefinitionsComponentState, - scopeId, - ), - visibleFieldDefinitionsState: extractComponentReadOnlySelector( - recordBoardVisibleFieldDefinitionsComponentSelector, - scopeId, - ), - - recordIdsByColumnIdFamilyState: extractComponentFamilyState( - recordBoardRecordIdsByColumnIdComponentFamilyState, - scopeId, - ), - isRecordBoardCardSelectedFamilyState: extractComponentFamilyState( - isRecordBoardCardSelectedComponentFamilyState, - scopeId, - ), - allRecordIdsSelector: extractComponentReadOnlySelector( - recordBoardAllRecordIdsComponentSelector, - scopeId, - ), - selectedRecordIdsSelector: extractComponentReadOnlySelector( - recordBoardSelectedRecordIdsComponentSelector, - scopeId, - ), - - isCompactModeActiveState: extractComponentState( - isRecordBoardCompactModeActiveComponentState, - scopeId, - ), - - shouldFetchMoreInColumnFamilyState: extractComponentFamilyState( - recordBoardShouldFetchMoreInColumnComponentFamilyState, - scopeId, - ), - shouldFetchMoreSelector: extractComponentReadOnlySelector( - recordBoardShouldFetchMoreComponentSelector, - scopeId, - ), - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts deleted file mode 100644 index 58e00b990..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { sortRecordGroupDefinitions } from '@/object-record/record-group/utils/sortRecordGroupDefinitions'; -import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; - -export const useSetRecordBoardColumns = (recordBoardId?: string) => { - const { scopeId, columnIdsState, columnsFamilySelector } = - useRecordBoardStates(recordBoardId); - - const recordGroupSort = useRecoilComponentValueV2( - recordIndexRecordGroupSortComponentState, - recordBoardId, - ); - - const setColumns = useRecoilCallback( - ({ set, snapshot }) => - (columns: RecordGroupDefinition[]) => { - const currentColumnsIds = snapshot - .getLoadable(columnIdsState) - .getValue(); - - const sortedColumns = sortRecordGroupDefinitions( - columns, - recordGroupSort, - ); - - const columnIds = sortedColumns - .filter(({ isVisible }) => isVisible) - .map(({ id }) => id); - - if (!isDeeplyEqual(currentColumnsIds, columnIds)) { - set(columnIdsState, columnIds); - } - - columns.forEach((column) => { - const currentColumn = snapshot - .getLoadable(columnsFamilySelector(column.id)) - .getValue(); - - if (isDeeplyEqual(currentColumn, column)) { - return; - } - - set(columnsFamilySelector(column.id), column); - }); - }, - [columnIdsState, recordGroupSort, columnsFamilySelector], - ); - - return { - scopeId, - setColumns, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts deleted file mode 100644 index e5b61e4e6..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; - -export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { - const { - scopeId, - recordIdsByColumnIdFamilyState, - columnsFamilySelector, - columnIdsState, - kanbanFieldMetadataNameState, - } = useRecordBoardStates(recordBoardId); - - const setRecordIds = useRecoilCallback( - ({ set, snapshot }) => - (records: ObjectRecord[]) => { - const columnIds = snapshot.getLoadable(columnIdsState).getValue(); - - columnIds.forEach((columnId) => { - const column = snapshot - .getLoadable(columnsFamilySelector(columnId)) - .getValue(); - - const existingColumnRecordIds = snapshot - .getLoadable(recordIdsByColumnIdFamilyState(columnId)) - .getValue(); - - const kanbanFieldMetadataName = snapshot - .getLoadable(kanbanFieldMetadataNameState) - .getValue(); - - if (!kanbanFieldMetadataName) { - return; - } - - const columnRecordIds = records - .filter( - (record) => record[kanbanFieldMetadataName] === column?.value, - ) - .sort(sortRecordsByPosition) - .map((record) => record.id); - - if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) { - set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds); - } - }); - }, - [ - columnIdsState, - columnsFamilySelector, - recordIdsByColumnIdFamilyState, - kanbanFieldMetadataNameState, - ], - ); - - return { - scopeId, - setRecordIds, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts deleted file mode 100644 index 25138622d..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useRecoilCallback } from 'recoil'; - -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { ObjectRecord } from '@/object-record/types/ObjectRecord'; -import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; -import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; - -export const useSetRecordIdsForColumn = (recordBoardId?: string) => { - const { - scopeId, - recordIdsByColumnIdFamilyState, - columnsFamilySelector, - kanbanFieldMetadataNameState, - } = useRecordBoardStates(recordBoardId); - - const setRecordIdsForColumn = useRecoilCallback( - ({ set, snapshot }) => - (columnId: string, records: ObjectRecord[]) => { - const column = snapshot - .getLoadable(columnsFamilySelector(columnId)) - .getValue(); - - const existingColumnRecordIds = snapshot - .getLoadable(recordIdsByColumnIdFamilyState(columnId)) - .getValue(); - - const kanbanFieldMetadataName = snapshot - .getLoadable(kanbanFieldMetadataNameState) - .getValue(); - - if (!kanbanFieldMetadataName) { - return; - } - - const columnRecordIds = records - .filter((record) => record[kanbanFieldMetadataName] === column?.value) - .sort(sortRecordsByPosition) - .map((record) => record.id); - - if (!isDeeplyEqual(existingColumnRecordIds, columnRecordIds)) { - set(recordIdsByColumnIdFamilyState(columnId), columnRecordIds); - } - }, - [ - columnsFamilySelector, - recordIdsByColumnIdFamilyState, - kanbanFieldMetadataNameState, - ], - ); - - return { - scopeId, - setRecordIdsForColumn, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts deleted file mode 100644 index 755703d0e..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { useSetRecoilState } from 'recoil'; - -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { useSetRecordBoardColumns } from '@/object-record/record-board/hooks/internal/useSetRecordBoardColumns'; -import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds'; -import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/internal/useSetRecordIdsForColumn'; - -export const useRecordBoard = (recordBoardId?: string) => { - const { - scopeId, - fieldDefinitionsState, - objectSingularNameState, - selectedRecordIdsSelector, - isCompactModeActiveState, - kanbanFieldMetadataNameState, - shouldFetchMoreSelector, - isFetchingRecordsByColumnState, - } = useRecordBoardStates(recordBoardId); - - const { setColumns } = useSetRecordBoardColumns(recordBoardId); - const { setRecordIds } = useSetRecordBoardRecordIds(recordBoardId); - const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId); - - const setFieldDefinitions = useSetRecoilState(fieldDefinitionsState); - const setObjectSingularName = useSetRecoilState(objectSingularNameState); - const setKanbanFieldMetadataName = useSetRecoilState( - kanbanFieldMetadataNameState, - ); - - return { - scopeId, - setColumns, - setRecordIds, - setFieldDefinitions, - setObjectSingularName, - setKanbanFieldMetadataName, - selectedRecordIdsSelector, - isCompactModeActiveState, - shouldFetchMoreSelector, - setRecordIdsForColumn, - isFetchingRecordsByColumnState, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts index baba7e9f9..8c20d3933 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoardSelection.ts @@ -2,13 +2,25 @@ import { useRecoilCallback } from 'recoil'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; +import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; export const useRecordBoardSelection = (recordBoardId: string) => { - const { selectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState } = - useRecordBoardStates(recordBoardId); + const isRecordBoardCardSelectedFamilyState = + useRecoilComponentCallbackStateV2( + isRecordBoardCardSelectedComponentFamilyState, + recordBoardId, + ); + + const recordBoardSelectedRecordIdsSelector = + useRecoilComponentCallbackStateV2( + recordBoardSelectedRecordIdsComponentSelector, + recordBoardId, + ); const isActionMenuDropdownOpenState = extractComponentState( isDropdownOpenComponentState, @@ -22,9 +34,10 @@ export const useRecordBoardSelection = (recordBoardId: string) => { () => { set(isActionMenuDropdownOpenState, false); - const recordIds = snapshot - .getLoadable(selectedRecordIdsSelector()) - .getValue(); + const recordIds = getSnapshotValue( + snapshot, + recordBoardSelectedRecordIdsSelector, + ); for (const recordId of recordIds) { set(isRecordBoardCardSelectedFamilyState(recordId), false); @@ -32,7 +45,7 @@ export const useRecordBoardSelection = (recordBoardId: string) => { }, [ isActionMenuDropdownOpenState, - selectedRecordIdsSelector, + recordBoardSelectedRecordIdsSelector, isRecordBoardCardSelectedFamilyState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordBoardRecordIds.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordBoardRecordIds.ts new file mode 100644 index 000000000..0194a5bf3 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordBoardRecordIds.ts @@ -0,0 +1,110 @@ +import { useRecoilCallback } from 'recoil'; + +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { isDefined } from '~/utils/isDefined'; + +export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { + const visibleRecordGroupIdsSelector = useRecoilComponentCallbackStateV2( + visibleRecordGroupIdsComponentSelector, + ); + + const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2( + recordGroupFieldMetadataComponentState, + recordBoardId, + ); + + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, + recordBoardId, + ); + + const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( + recordIndexRowIdsByGroupComponentFamilyState, + recordBoardId, + ); + + const setRecordIds = useRecoilCallback( + ({ set, snapshot }) => + (records: ObjectRecord[]) => { + const existingAllRowIds = getSnapshotValue( + snapshot, + recordIndexAllRowIdsState, + ); + + const recordGroupIds = getSnapshotValue( + snapshot, + visibleRecordGroupIdsSelector, + ); + + for (const recordGroupId of recordGroupIds) { + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroupId), + ); + + const existingRecordGroupRowIds = getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(recordGroupId), + ); + + const recordGroupFieldMetadata = getSnapshotValue( + snapshot, + recordGroupFieldMetadataState, + ); + + if (!isDefined(recordGroupFieldMetadata)) { + return; + } + + const recordGroupRowIds = records + .filter( + (record) => + record[recordGroupFieldMetadata.name] === recordGroup?.value, + ) + .sort(sortRecordsByPosition) + .map((record) => record.id); + + if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) { + set( + recordIndexRowIdsByGroupFamilyState(recordGroupId), + recordGroupRowIds, + ); + } + } + + const allRowIds: string[] = []; + + for (const recordGroupId of recordGroupIds) { + const tableRowIdsByGroup = getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(recordGroupId), + ); + + allRowIds.push(...tableRowIdsByGroup); + } + + if (!isDeeplyEqual(existingAllRowIds, allRowIds)) { + set(recordIndexAllRowIdsState, allRowIds); + } + }, + [ + visibleRecordGroupIdsSelector, + recordIndexRowIdsByGroupFamilyState, + recordGroupFieldMetadataState, + recordIndexAllRowIdsState, + ], + ); + + return { + setRecordIds, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordIdsForColumn.ts b/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordIdsForColumn.ts new file mode 100644 index 000000000..3ba7c3ee7 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordIdsForColumn.ts @@ -0,0 +1,109 @@ +import { useRecoilCallback } from 'recoil'; + +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { isDefined } from '~/utils/isDefined'; + +export const useSetRecordIdsForColumn = (recordBoardId?: string) => { + const recordGroupIdsState = useRecoilComponentCallbackStateV2( + recordGroupIdsComponentState, + recordBoardId, + ); + + const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2( + recordGroupFieldMetadataComponentState, + recordBoardId, + ); + + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, + recordBoardId, + ); + + const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( + recordIndexRowIdsByGroupComponentFamilyState, + recordBoardId, + ); + + const setRecordIdsForColumn = useRecoilCallback( + ({ set, snapshot }) => + (currentRecordGroupId: string, records: ObjectRecord[]) => { + const existingAllRowIds = getSnapshotValue( + snapshot, + recordIndexAllRowIdsState, + ); + + const recordGroupIds = getSnapshotValue(snapshot, recordGroupIdsState); + + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(currentRecordGroupId), + ); + + const existingRecordGroupRowIds = getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + ); + + const recordGroupFieldMetadata = getSnapshotValue( + snapshot, + recordGroupFieldMetadataState, + ); + + if (!isDefined(recordGroupFieldMetadata)) { + return; + } + + const recordGroupRowIds = records + .filter( + (record) => + record[recordGroupFieldMetadata.name] === recordGroup?.value, + ) + .sort(sortRecordsByPosition) + .map((record) => record.id); + + if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) { + set( + recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + recordGroupRowIds, + ); + } + + const allRowIds: string[] = []; + + for (const recordGroupId of recordGroupIds) { + const tableRowIdsByGroup = + recordGroupId !== currentRecordGroupId + ? getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(recordGroupId), + ) + : recordGroupRowIds; + + allRowIds.push(...tableRowIdsByGroup); + } + + if (!isDeeplyEqual(existingAllRowIds, allRowIds)) { + set(recordIndexAllRowIdsState, allRowIds); + } + }, + [ + recordGroupIdsState, + recordIndexRowIdsByGroupFamilyState, + recordGroupFieldMetadataState, + recordIndexAllRowIdsState, + ], + ); + + return { + setRecordIdsForColumn, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx index 92d897a61..a456c89a3 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-card/components/RecordBoardCard.tsx @@ -3,9 +3,11 @@ import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-me import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext'; import { RecordBoardScopeInternalContext } from '@/object-record/record-board/scopes/scope-internal-context/RecordBoardScopeInternalContext'; +import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; +import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; +import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; import { FieldContext, RecordUpdateHook, @@ -22,11 +24,13 @@ import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { TextInput } from '@/ui/input/components/TextInput'; import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId'; import { RecordBoardScrollWrapperContext } from '@/ui/utilities/scroll/contexts/ScrollWrapperContexts'; +import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import styled from '@emotion/styled'; import { ReactNode, useContext, useState } from 'react'; import { InView, useInView } from 'react-intersection-observer'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import { AnimatedEaseInOut, AvatarChipVariant, @@ -157,27 +161,30 @@ export const RecordBoardCard = ({ onCreateSuccess?: () => void; position?: 'first' | 'last'; }) => { - const [newLabelValue, setNewLabelValue] = useState(''); - const { handleBlur, handleInputEnter } = useAddNewCard(); const { recordId } = useContext(RecordBoardCardContext); + + const [newLabelValue, setNewLabelValue] = useState(''); + + const { handleBlur, handleInputEnter } = useAddNewCard(); + const { updateOneRecord, objectMetadataItem } = useContext(RecordBoardContext); - const { - isCompactModeActiveState, - isRecordBoardCardSelectedFamilyState, - visibleFieldDefinitionsState, - } = useRecordBoardStates(); - const isCompactModeActive = useRecoilValue(isCompactModeActiveState); + + const visibleFieldDefinitions = useRecoilComponentValueV2( + recordBoardVisibleFieldDefinitionsComponentSelector, + ); + + const isCompactModeActive = useRecoilComponentValueV2( + isRecordBoardCompactModeActiveComponentState, + ); const [isCardExpanded, setIsCardExpanded] = useState(false); - const [isCurrentCardSelected, setIsCurrentCardSelected] = useRecoilState( - isRecordBoardCardSelectedFamilyState(recordId), - ); - - const visibleFieldDefinitions = useRecoilValue( - visibleFieldDefinitionsState(), - ); + const [isCurrentCardSelected, setIsCurrentCardSelected] = + useRecoilComponentFamilyStateV2( + isRecordBoardCardSelectedComponentFamilyState, + recordId, + ); const record = useRecoilValue(recordStoreFamilyState(recordId)); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx index 26072d4a7..1d850a850 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumn.tsx @@ -1,10 +1,12 @@ import styled from '@emotion/styled'; import { Droppable } from '@hello-pangea/dnd'; -import { useRecoilValue } from 'recoil'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumnCardsContainer } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; +import { useRecoilValue } from 'recoil'; const StyledColumn = styled.div` background-color: ${({ theme }) => theme.background.primary}; @@ -25,27 +27,26 @@ type RecordBoardColumnProps = { export const RecordBoardColumn = ({ recordBoardColumnId, }: RecordBoardColumnProps) => { - const { columnsFamilySelector, recordIdsByColumnIdFamilyState } = - useRecordBoardStates(); - const columnDefinition = useRecoilValue( - columnsFamilySelector(recordBoardColumnId), + const recordGroupDefinition = useRecoilValue( + recordGroupDefinitionFamilyState(recordBoardColumnId), ); - const recordIds = useRecoilValue( - recordIdsByColumnIdFamilyState(recordBoardColumnId), + const recordRowIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRowIdsByGroupComponentFamilyState, + recordBoardColumnId, ); - if (!columnDefinition) { + if (!recordGroupDefinition) { return null; } return ( @@ -53,7 +54,7 @@ export const RecordBoardColumn = ({ )} diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx index d3e540902..8cdc5c32f 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnCardsContainer.tsx @@ -5,7 +5,6 @@ import { useRecoilValue } from 'recoil'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumnCardContainerSkeletonLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardContainerSkeletonLoader'; import { RecordBoardColumnCardsMemo } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnCardsMemo'; import { RecordBoardColumnFetchMoreLoader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader'; @@ -15,7 +14,10 @@ import { RecordBoardColumnNewRecordButton } from '@/object-record/record-board/r import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; import { getNumberOfCardsPerColumnForSkeletonLoading } from '@/object-record/record-board/record-board-column/utils/getNumberOfCardsPerColumnForSkeletonLoading'; +import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; +import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; const StyledColumnCardsContainer = styled.div` display: flex; @@ -56,16 +58,16 @@ export const RecordBoardColumnCardsContainer = ({ isRecordIndexBoardColumnLoadingFamilyState(columnId), ); - const { isCompactModeActiveState, visibleFieldDefinitionsState } = - useRecordBoardStates(); - - const visibleFieldDefinitions = useRecoilValue( - visibleFieldDefinitionsState(), + const visibleFieldDefinitions = useRecoilComponentValueV2( + recordBoardVisibleFieldDefinitionsComponentSelector, ); const numberOfFields = visibleFieldDefinitions.length; - const isCompactModeActive = useRecoilValue(isCompactModeActiveState); + const isCompactModeActive = useRecoilComponentValueV2( + isRecordBoardCompactModeActiveComponentState, + ); + const { isOpportunitiesCompanyFieldDisabled } = useIsOpportunitiesCompanyFieldDisabled(); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx index ed6045033..bfbfb7e9d 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx @@ -5,7 +5,6 @@ import { useRecordGroupActions } from '@/object-record/record-group/hooks/useRec import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; -import { ViewType } from '@/views/types/ViewType'; import { MenuItem } from 'twenty-ui'; const StyledMenuContainer = styled.div` @@ -26,9 +25,7 @@ export const RecordBoardColumnDropdownMenu = ({ }: RecordBoardColumnDropdownMenuProps) => { const boardColumnMenuRef = useRef(null); - const recordGroupActions = useRecordGroupActions({ - viewType: ViewType.Kanban, - }); + const recordGroupActions = useRecordGroupActions(); const closeMenu = useCallback(() => { onClose(); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx index 7a610c3dd..1143aaf5e 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnFetchMoreLoader.tsx @@ -1,11 +1,13 @@ +import styled from '@emotion/styled'; import { useContext, useEffect } from 'react'; import { useInView } from 'react-intersection-observer'; -import styled from '@emotion/styled'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { GRAY_SCALE } from 'twenty-ui'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState'; +import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; +import { useSetRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentFamilyStateV2'; const StyledText = styled.div` align-items: center; @@ -19,15 +21,14 @@ const StyledText = styled.div` export const RecordBoardColumnFetchMoreLoader = () => { const { columnDefinition } = useContext(RecordBoardColumnContext); - const { shouldFetchMoreInColumnFamilyState, isFetchingRecordsByColumnState } = - useRecordBoardStates(); const isFetchingRecord = useRecoilValue( - isFetchingRecordsByColumnState({ columnId: columnDefinition.id }), + isRecordBoardFetchingRecordsByColumnFamilyState(columnDefinition.id), ); - const setShouldFetchMore = useSetRecoilState( - shouldFetchMoreInColumnFamilyState(columnDefinition.id), + const setShouldFetchMore = useSetRecoilComponentFamilyStateV2( + recordBoardShouldFetchMoreInColumnComponentFamilyState, + columnDefinition.id, ); const { ref, inView } = useInView(); diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper.tsx index 1386b494d..68de7adb5 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderWrapper.tsx @@ -1,8 +1,10 @@ import { isDefined } from 'twenty-ui'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { RecordBoardColumnHeader } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeader'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilValue } from 'recoil'; type RecordBoardColumnHeaderWrapperProps = { @@ -12,14 +14,16 @@ type RecordBoardColumnHeaderWrapperProps = { export const RecordBoardColumnHeaderWrapper = ({ columnId, }: RecordBoardColumnHeaderWrapperProps) => { - const { columnsFamilySelector, recordIdsByColumnIdFamilyState } = - useRecordBoardStates(); + const recordGroupDefinition = useRecoilValue( + recordGroupDefinitionFamilyState(columnId), + ); - const columnDefinition = useRecoilValue(columnsFamilySelector(columnId)); + const recordRowIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRowIdsByGroupComponentFamilyState, + columnId, + ); - const recordIds = useRecoilValue(recordIdsByColumnIdFamilyState(columnId)); - - if (!isDefined(columnDefinition)) { + if (!isDefined(recordGroupDefinition)) { return null; } @@ -27,9 +31,9 @@ export const RecordBoardColumnHeaderWrapper = ({ diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts index cb087c081..3926ceddb 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useColumnNewCardActions.ts @@ -1,12 +1,12 @@ -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; -import { useRecoilValue } from 'recoil'; +import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const useColumnNewCardActions = (columnId: string) => { - const { visibleFieldDefinitionsState } = useRecordBoardStates(); - const visibleFieldDefinitions = useRecoilValue( - visibleFieldDefinitionsState(), + const visibleFieldDefinitions = useRecoilComponentValueV2( + recordBoardVisibleFieldDefinitionsComponentSelector, ); + const labelIdentifierField = visibleFieldDefinitions.find( (field) => field.isLabelIdentifier, ); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext.ts b/packages/twenty-front/src/modules/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext.ts new file mode 100644 index 000000000..1d83aab21 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext.ts @@ -0,0 +1,4 @@ +import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext'; + +export const RecordBoardComponentInstanceContext = + createComponentInstanceContext(); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState.ts index 8cb7c99f4..3b446676d 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState.ts @@ -1,7 +1,9 @@ -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; +import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; export const isRecordBoardCardSelectedComponentFamilyState = - createComponentFamilyState({ + createComponentFamilyStateV2({ key: 'isRecordBoardCardSelectedComponentFamilyState', defaultValue: false, + componentInstanceContext: RecordBoardComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState.ts index 68741ee81..bde53d616 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState.ts @@ -1,7 +1,9 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; export const isRecordBoardCompactModeActiveComponentState = - createComponentState({ + createComponentStateV2({ key: 'isRecordBoardCompactModeActiveComponentState', defaultValue: false, + componentInstanceContext: RecordBoardComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState.ts index dfaaffc0e..8ee4affb3 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsByColumnFamilyState.ts @@ -1,7 +1,10 @@ -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { atomFamily } from 'recoil'; -export const isRecordBoardFetchingRecordsByColumnFamilyState = - createComponentFamilyState({ - key: 'isRecordBoardFetchingRecordsByColumnFamilyState', - defaultValue: false, - }); +export const isRecordBoardFetchingRecordsByColumnFamilyState = atomFamily< + boolean, + RecordGroupDefinition['id'] +>({ + key: 'isRecordBoardFetchingRecordsByColumnFamilyState', + default: false, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState.ts deleted file mode 100644 index c76a8777e..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const isRecordBoardFetchingRecordsComponentState = - createComponentState({ - key: 'isRecordBoardFetchingRecordsComponentState', - defaultValue: false, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnIdsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnIdsComponentState.ts deleted file mode 100644 index 3ae094376..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnIdsComponentState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const recordBoardColumnIdsComponentState = createComponentState< - string[] ->({ - key: 'recordBoardColumnIdsComponentState', - defaultValue: [], -}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnsComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnsComponentFamilyState.ts deleted file mode 100644 index 1530820d8..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnsComponentFamilyState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const recordBoardColumnsComponentFamilyState = - createComponentFamilyState({ - key: 'recordBoardColumnsComponentFamilyState', - defaultValue: undefined, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsComponentState.ts index e8fb862be..36696c000 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFieldDefinitionsComponentState.ts @@ -1,10 +1,12 @@ +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { RecordBoardFieldDefinition } from '@/object-record/record-board/types/RecordBoardFieldDefinition'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -export const recordBoardFieldDefinitionsComponentState = createComponentState< +export const recordBoardFieldDefinitionsComponentState = createComponentStateV2< RecordBoardFieldDefinition[] >({ key: 'recordBoardFieldDefinitionsComponentState', defaultValue: [], + componentInstanceContext: RecordBoardComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersComponentState.ts deleted file mode 100644 index 7d493b349..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersComponentState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const recordBoardFiltersComponentState = createComponentState({ - key: 'recordBoardFiltersComponentState', - defaultValue: [], -}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState.ts deleted file mode 100644 index 26490c929..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const recordBoardKanbanFieldMetadataNameComponentState = - createComponentState({ - key: 'recordBoardKanbanFieldMetadataNameComponentState', - defaultValue: undefined, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState.ts deleted file mode 100644 index 9cb2f0df3..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; - -export const recordBoardRecordIdsByColumnIdComponentFamilyState = - createComponentFamilyState({ - key: 'recordBoardRecordIdsByColumnIdComponentFamilyState', - defaultValue: [], - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState.ts index 01dd19099..2dec1d6b8 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState.ts @@ -1,7 +1,9 @@ -import { createComponentFamilyState } from '@/ui/utilities/state/component-state/utils/createComponentFamilyState'; +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; +import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; export const recordBoardShouldFetchMoreInColumnComponentFamilyState = - createComponentFamilyState({ - key: 'onRecordBoardFetchMoreIrecordBoardShouldFetchMoreInColumnComponentFamilyStatesVisibleComponentFamilyState', + createComponentFamilyStateV2({ + key: 'recordBoardShouldFetchMoreInColumnComponentFamilyState', defaultValue: false, + componentInstanceContext: RecordBoardComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsComponentState.ts deleted file mode 100644 index d2aa0923f..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsComponentState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Sort } from '@/object-record/object-sort-dropdown/types/Sort'; -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; - -export const recordBoardSortsComponentState = createComponentState({ - key: 'recordBoardSortsComponentState', - defaultValue: [], -}); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts deleted file mode 100644 index 154da9313..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState'; -import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; -import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector'; - -export const recordBoardAllRecordIdsComponentSelector = - createComponentReadOnlySelector({ - key: 'recordBoardAllRecordIdsComponentSelector', - get: - ({ scopeId }) => - ({ get }) => { - const columnIds = get(recordBoardColumnIdsComponentState({ scopeId })); - - const recordIdsByColumn = columnIds.map((columnId) => - get( - recordBoardRecordIdsByColumnIdComponentFamilyState({ - scopeId, - familyKey: columnId, - }), - ), - ); - - const recordIds = recordIdsByColumn.flat(); - - return recordIds; - }, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector.ts deleted file mode 100644 index fefff8451..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { recordBoardColumnsComponentFamilyState } from '@/object-record/record-board/states/recordBoardColumnsComponentFamilyState'; -import { createComponentFamilySelector } from '@/ui/utilities/state/component-state/utils/createComponentFamilySelector'; -import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; - -export const recordBoardColumnsComponentFamilySelector = - createComponentFamilySelector({ - key: 'recordBoardColumnsComponentFamilySelector', - get: - ({ - scopeId, - familyKey: columnId, - }: { - scopeId: string; - familyKey: string; - }) => - ({ get }) => { - return get( - recordBoardColumnsComponentFamilyState({ - scopeId, - familyKey: columnId, - }), - ); - }, - set: - ({ - scopeId, - familyKey: columnId, - }: { - scopeId: string; - familyKey: string; - }) => - ({ set }, newColumn) => { - set( - recordBoardColumnsComponentFamilyState({ - scopeId, - familyKey: columnId, - }), - newColumn, - ); - }, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector.ts index ce2b1e4ca..849d45735 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector.ts @@ -1,32 +1,24 @@ +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; -import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState'; -import { recordBoardRecordIdsByColumnIdComponentFamilyState } from '@/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState'; -import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; export const recordBoardSelectedRecordIdsComponentSelector = - createComponentReadOnlySelector({ + createComponentSelectorV2({ key: 'recordBoardSelectedRecordIdsSelector', + componentInstanceContext: RecordBoardComponentInstanceContext, get: - ({ scopeId }) => + ({ instanceId }) => ({ get }) => { - const columnIds = get(recordBoardColumnIdsComponentState({ scopeId })); - - const recordIdsByColumn = columnIds.map((columnId) => - get( - recordBoardRecordIdsByColumnIdComponentFamilyState({ - scopeId, - familyKey: columnId, - }), - ), + const allRowIds = get( + recordIndexAllRowIdsComponentState.atomFamily({ instanceId }), ); - const recordIds = recordIdsByColumn.flat(); - - return recordIds.filter( + return allRowIds.filter( (recordId) => get( - isRecordBoardCardSelectedComponentFamilyState({ - scopeId, + isRecordBoardCardSelectedComponentFamilyState.atomFamily({ + instanceId, familyKey: recordId, }), ) === true, diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector.ts deleted file mode 100644 index 225ffafac..000000000 --- a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { recordBoardColumnIdsComponentState } from '@/object-record/record-board/states/recordBoardColumnIdsComponentState'; -import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; -import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector'; - -export const recordBoardShouldFetchMoreComponentSelector = - createComponentReadOnlySelector({ - key: 'recordBoardShouldFetchMoreComponentSelector', - get: - ({ scopeId }: { scopeId: string }) => - ({ get }) => { - const columnIds = get( - recordBoardColumnIdsComponentState({ - scopeId, - }), - ); - - const shouldFetchMoreInColumns = columnIds.map((columnId) => { - return get( - recordBoardShouldFetchMoreInColumnComponentFamilyState({ - scopeId, - familyKey: columnId, - }), - ); - }); - - return shouldFetchMoreInColumns.some(Boolean); - }, - }); diff --git a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector.ts index 4b2732eb3..62ae69407 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector.ts @@ -1,13 +1,17 @@ +import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; -import { createComponentReadOnlySelector } from '@/ui/utilities/state/component-state/utils/createComponentReadOnlySelector'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; export const recordBoardVisibleFieldDefinitionsComponentSelector = - createComponentReadOnlySelector({ + createComponentSelectorV2({ key: 'recordBoardVisibleFieldDefinitionsComponentSelector', get: - ({ scopeId }) => + ({ instanceId }) => ({ get }) => - get(recordBoardFieldDefinitionsComponentState({ scopeId })) + get( + recordBoardFieldDefinitionsComponentState.atomFamily({ instanceId }), + ) .filter((field) => field.isVisible) .sort((a, b) => a.position - b.position), + componentInstanceContext: RecordBoardComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupMenuItemDraggable.tsx b/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupMenuItemDraggable.tsx index 70bd645af..3a5d668ad 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupMenuItemDraggable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupMenuItemDraggable.tsx @@ -1,31 +1,45 @@ import { IconEye, IconEyeOff, MenuItemDraggable, Tag } from 'twenty-ui'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { RecordGroupDefinition, RecordGroupDefinitionType, } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { useRecoilValue } from 'recoil'; import { isDefined } from '~/utils/isDefined'; type RecordGroupMenuItemDraggableProps = { - recordGroup: RecordGroupDefinition; + recordGroupId: string; showDragGrip?: boolean; isDraggable?: boolean; - onVisibilityChange: (viewGroup: RecordGroupDefinition) => void; + onVisibilityChange: (recordGroup: RecordGroupDefinition) => void; }; export const RecordGroupMenuItemDraggable = ({ - recordGroup, + recordGroupId, showDragGrip, isDraggable, onVisibilityChange, }: RecordGroupMenuItemDraggableProps) => { + const recordGroup = useRecoilValue( + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroup)) { + return null; + } + const isNoValue = recordGroup.type === RecordGroupDefinitionType.NoValue; const getIconButtons = (recordGroup: RecordGroupDefinition) => { const iconButtons = [ { Icon: recordGroup.isVisible ? IconEyeOff : IconEye, - onClick: () => onVisibilityChange(recordGroup), + onClick: () => + onVisibilityChange({ + ...recordGroup, + isVisible: !recordGroup.isVisible, + }), }, ].filter(isDefined); diff --git a/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupsVisibilityDropdownSection.tsx b/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupsVisibilityDropdownSection.tsx index e43afd1ee..e655a402d 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupsVisibilityDropdownSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-group/components/RecordGroupsVisibilityDropdownSection.tsx @@ -13,17 +13,17 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop import { StyledDropdownMenuSubheader } from '@/ui/layout/dropdown/components/StyledDropdownMenuSubheader'; type RecordGroupsVisibilityDropdownSectionProps = { - recordGroups: RecordGroupDefinition[]; + recordGroupIds: string[]; isDraggable: boolean; onDragEnd?: OnDragEndResponder; - onVisibilityChange: (viewGroup: RecordGroupDefinition) => void; + onVisibilityChange: (recordGroup: RecordGroupDefinition) => void; title: string; showSubheader?: boolean; showDragGrip: boolean; }; export const RecordGroupsVisibilityDropdownSection = ({ - recordGroups, + recordGroupIds, isDraggable, onDragEnd, onVisibilityChange, @@ -43,12 +43,13 @@ export const RecordGroupsVisibilityDropdownSection = ({ {title} )} - {!!recordGroups.length && ( + {recordGroupIds.length > 0 && ( <> {!isDraggable ? ( - recordGroups.map((recordGroup) => ( + recordGroupIds.map((recordGroupId) => ( - {recordGroups.map((recordGroup, index) => ( + {recordGroupIds.map((recordGroupId, index) => ( { +export const useCurrentRecordGroupDefinition = () => { const context = useContext(RecordGroupContext); - const hasRecordGroups = useRecoilComponentValueV2( - hasRecordGroupDefinitionsComponentSelector, - recordTableId, + const recordGroupDefinition = useRecoilValue( + recordGroupDefinitionFamilyState(context?.recordGroupId), ); - const recordGroupDefinitions = useRecoilComponentValueV2( - recordGroupDefinitionsComponentState, - recordTableId, - ); - - const recordGroupDefinition = useMemo(() => { - if (!hasRecordGroups) { - return undefined; - } - - if (!context) { - throw new Error( - 'useCurrentRecordGroupDefinition must be used within a RecordGroupContextProvider.', - ); - } - - return recordGroupDefinitions.find( - ({ id }) => id === context.recordGroupId, - ); - }, [context, hasRecordGroups, recordGroupDefinitions]); - return recordGroupDefinition; }; diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts index b99af28b6..b1bc9829d 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupActions.ts @@ -2,24 +2,18 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadata import { getFieldSlug } from '@/object-metadata/utils/getFieldSlug'; import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { useRecordGroupVisibility } from '@/object-record/record-group/hooks/useRecordGroupVisibility'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { RecordGroupAction } from '@/object-record/record-group/types/RecordGroupActions'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; -import { ViewType } from '@/views/types/ViewType'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useCallback, useContext, useMemo } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; import { IconEyeOff, IconSettings, isDefined } from 'twenty-ui'; -type UseRecordGroupActionsParams = { - viewType: ViewType; -}; - -export const useRecordGroupActions = ({ - viewType, -}: UseRecordGroupActionsParams) => { +export const useRecordGroupActions = () => { const navigate = useNavigate(); const location = useLocation(); @@ -35,14 +29,13 @@ export const useRecordGroupActions = ({ objectNameSingular, }); - const { viewGroupFieldMetadataItem } = useRecordGroups({ - objectNameSingular, - }); + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + ); const { handleVisibilityChange: handleRecordGroupVisibilityChange } = useRecordGroupVisibility({ viewBarId: recordIndexId, - viewType, }); const setNavigationMemorizedUrl = useSetRecoilState( @@ -52,11 +45,11 @@ export const useRecordGroupActions = ({ const navigateToSelectSettings = useCallback(() => { setNavigationMemorizedUrl(location.pathname + location.search); - if (!isDefined(viewGroupFieldMetadataItem)) { - throw new Error('viewGroupFieldMetadataItem is not a non-empty string'); + if (!isDefined(recordGroupFieldMetadata)) { + throw new Error('recordGroupFieldMetadata is not a non-empty string'); } - const settingsPath = `/settings/objects/${getObjectSlug(objectMetadataItem)}/${getFieldSlug(viewGroupFieldMetadataItem)}`; + const settingsPath = `/settings/objects/${getObjectSlug(objectMetadataItem)}/${getFieldSlug(recordGroupFieldMetadata)}`; navigate(settingsPath); }, [ @@ -65,7 +58,7 @@ export const useRecordGroupActions = ({ location.search, navigate, objectMetadataItem, - viewGroupFieldMetadataItem, + recordGroupFieldMetadata, ]); const recordGroupActions: RecordGroupAction[] = useMemo( diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupReorder.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupReorder.ts index b0c738a0f..9b1c038e1 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupReorder.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupReorder.ts @@ -1,59 +1,89 @@ import { OnDragEndResponder } from '@hello-pangea/dnd'; -import { useCallback } from 'react'; -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; -import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; +import { useRecoilCallback } from 'recoil'; import { moveArrayItem } from '~/utils/array/moveArrayItem'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { isDefined } from '~/utils/isDefined'; type UseRecordGroupHandlersParams = { - objectNameSingular: string; viewBarId: string; }; export const useRecordGroupReorder = ({ - objectNameSingular, viewBarId, }: UseRecordGroupHandlersParams) => { - const setRecordGroupDefinitions = useSetRecoilComponentStateV2( - recordGroupDefinitionsComponentState, - ); + const setRecordGroup = useSetRecordGroup(viewBarId); - const { visibleRecordGroups } = useRecordGroups({ - objectNameSingular: objectNameSingular, - }); + const visibleRecordGroupIdsSelector = useRecoilComponentCallbackStateV2( + visibleRecordGroupIdsComponentSelector, + ); const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); - const handleOrderChange: OnDragEndResponder = useCallback( - (result) => { - if (!result.destination) { - return; - } + const handleOrderChange: OnDragEndResponder = useRecoilCallback( + ({ snapshot }) => + (result) => { + if (!result.destination) { + return; + } - const reorderedVisibleBoardGroups = moveArrayItem(visibleRecordGroups, { - fromIndex: result.source.index - 1, - toIndex: result.destination.index - 1, - }); + const visibleRecordGroupIds = getSnapshotValue( + snapshot, + visibleRecordGroupIdsSelector, + ); - if (isDeeplyEqual(visibleRecordGroups, reorderedVisibleBoardGroups)) - return; + const reorderedVisibleRecordGroupIds = moveArrayItem( + visibleRecordGroupIds, + { + fromIndex: result.source.index - 1, + toIndex: result.destination.index - 1, + }, + ); - const updatedGroups = [...reorderedVisibleBoardGroups].map( - (group, index) => ({ ...group, position: index }), - ); + if ( + isDeeplyEqual(visibleRecordGroupIds, reorderedVisibleRecordGroupIds) + ) { + return; + } - setRecordGroupDefinitions(updatedGroups); - saveViewGroups(mapRecordGroupDefinitionsToViewGroups(updatedGroups)); - }, - [saveViewGroups, setRecordGroupDefinitions, visibleRecordGroups], + const updatedRecordGroups = reorderedVisibleRecordGroupIds.reduce< + RecordGroupDefinition[] + >((acc, recordGroupId, index) => { + const recordGroupDefinition = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroupDefinition)) { + return acc; + } + + return [ + ...acc, + { + ...recordGroupDefinition, + position: index, + }, + ]; + }, []); + + setRecordGroup(updatedRecordGroups); + saveViewGroups( + mapRecordGroupDefinitionsToViewGroups(updatedRecordGroups), + ); + }, + [saveViewGroups, setRecordGroup, visibleRecordGroupIdsSelector], ); return { - visibleRecordGroups, handleOrderChange, }; }; diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts index 5f877bff3..aa26edec3 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroupVisibility.ts @@ -1,125 +1,113 @@ -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { recordIndexRecordGroupHideComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupHideComponentState'; -import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; -import { ViewType } from '@/views/types/ViewType'; import { mapRecordGroupDefinitionsToViewGroups } from '@/views/utils/mapRecordGroupDefinitionsToViewGroups'; +import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup'; import { useRecoilCallback } from 'recoil'; +import { isDefined } from '~/utils/isDefined'; type UseRecordGroupVisibilityParams = { viewBarId: string; - viewType: ViewType; }; export const useRecordGroupVisibility = ({ viewBarId, - viewType, }: UseRecordGroupVisibilityParams) => { - const recordGroupDefinitionsState = useRecoilComponentCallbackStateV2( - recordGroupDefinitionsComponentState, + const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( + recordGroupIdsComponentState, ); - const tableRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - tableRowIdsByGroupComponentFamilyState, + const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( + recordIndexRowIdsByGroupComponentFamilyState, viewBarId, ); - const { recordIdsByColumnIdFamilyState } = useRecordBoardStates(viewBarId); - const objectOptionsDropdownRecordGroupHideState = useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); - const { saveViewGroups } = useSaveCurrentViewGroups(viewBarId); + const { saveViewGroup, saveViewGroups } = useSaveCurrentViewGroups(viewBarId); const handleVisibilityChange = useRecoilCallback( - ({ snapshot, set }) => - async (updatedRecordGroupDefinition: RecordGroupDefinition) => { - const recordGroupDefinitions = getSnapshotValue( - snapshot, - recordGroupDefinitionsState, + ({ set }) => + async (updatedRecordGroup: RecordGroupDefinition) => { + set( + recordGroupDefinitionFamilyState(updatedRecordGroup.id), + updatedRecordGroup, ); - const updatedRecordGroupDefinitions = recordGroupDefinitions.map( - (groupDefinition) => - groupDefinition.id === updatedRecordGroupDefinition.id - ? { - ...groupDefinition, - isVisible: !groupDefinition.isVisible, - } - : groupDefinition, - ); - - set(recordGroupDefinitionsState, updatedRecordGroupDefinitions); - - saveViewGroups( - mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), - ); + saveViewGroup(recordGroupDefinitionToViewGroup(updatedRecordGroup)); // If visibility is manually toggled, we should reset the hideEmptyRecordGroup state set(objectOptionsDropdownRecordGroupHideState, false); }, - [ - objectOptionsDropdownRecordGroupHideState, - recordGroupDefinitionsState, - saveViewGroups, - ], + [saveViewGroup, objectOptionsDropdownRecordGroupHideState], ); const handleHideEmptyRecordGroupChange = useRecoilCallback( ({ snapshot, set }) => async () => { - const recordGroupDefinitions = getSnapshotValue( + const updatedRecordGroupDefinitions: RecordGroupDefinition[] = []; + const recordGroupIds = getSnapshotValue( snapshot, - recordGroupDefinitionsState, + recordIndexRecordGroupIdsState, ); const currentHideState = getSnapshotValue( snapshot, objectOptionsDropdownRecordGroupHideState, ); + const newHideState = !currentHideState; - set(objectOptionsDropdownRecordGroupHideState, !currentHideState); + set(objectOptionsDropdownRecordGroupHideState, newHideState); - const updatedRecordGroupDefinitions = recordGroupDefinitions.map( - (recordGroup) => { - // TODO: Maybe we can improve that and only use one state for both table and board - const recordGroupRowIds = - viewType === ViewType.Table - ? getSnapshotValue( - snapshot, - tableRowIdsByGroupFamilyState(recordGroup.id), - ) - : getSnapshotValue( - snapshot, - recordIdsByColumnIdFamilyState(recordGroup.id), - ); + for (const recordGroupId of recordGroupIds) { + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroupId), + ); - if (recordGroupRowIds.length > 0) { - return recordGroup; - } + if (!isDefined(recordGroup)) { + throw new Error( + `Record group with id ${recordGroupId} not found in snapshot`, + ); + } - return { - ...recordGroup, - isVisible: currentHideState, - }; - }, - ); + const recordGroupRowIds = getSnapshotValue( + snapshot, + recordIndexRowIdsByGroupFamilyState(recordGroupId), + ); + + if (recordGroupRowIds.length > 0) { + continue; + } + + const updatedRecordGroup = { + ...recordGroup, + isVisible: !newHideState, + }; + + set( + recordGroupDefinitionFamilyState(recordGroupId), + updatedRecordGroup, + ); + + updatedRecordGroupDefinitions.push(updatedRecordGroup); + } saveViewGroups( mapRecordGroupDefinitionsToViewGroups(updatedRecordGroupDefinitions), ); }, [ - recordGroupDefinitionsState, + recordIndexRecordGroupIdsState, objectOptionsDropdownRecordGroupHideState, saveViewGroups, - viewType, - tableRowIdsByGroupFamilyState, - recordIdsByColumnIdFamilyState, + recordIndexRowIdsByGroupFamilyState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroups.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroups.ts deleted file mode 100644 index 8f638dbbe..000000000 --- a/packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroups.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useMemo } from 'react'; - -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; -import { sortRecordGroupDefinitions } from '@/object-record/record-group/utils/sortRecordGroupDefinitions'; -import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; - -type UseRecordGroupsParams = { - objectNameSingular: string; -}; - -export const useRecordGroups = ({ - objectNameSingular, -}: UseRecordGroupsParams) => { - const recordGroupDefinitions = useRecoilComponentValueV2( - recordGroupDefinitionsComponentState, - ); - - const recordGroupSort = useRecoilComponentValueV2( - recordIndexRecordGroupSortComponentState, - ); - - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - - const viewGroupFieldMetadataItem = useMemo(() => { - if (recordGroupDefinitions.length === 0) return null; - // We're assuming that all groups have the same fieldMetadataId for now - const fieldMetadataId = - 'fieldMetadataId' in recordGroupDefinitions[0] - ? recordGroupDefinitions[0].fieldMetadataId - : null; - - if (!fieldMetadataId) return null; - - return objectMetadataItem.fields.find( - (field) => field.id === fieldMetadataId, - ); - }, [objectMetadataItem, recordGroupDefinitions]); - - const visibleRecordGroups = useMemo( - () => sortRecordGroupDefinitions(recordGroupDefinitions, recordGroupSort), - [recordGroupDefinitions, recordGroupSort], - ); - - const hiddenRecordGroups = useMemo( - () => recordGroupDefinitions.filter((boardGroup) => !boardGroup.isVisible), - [recordGroupDefinitions], - ); - - return { - hiddenRecordGroups, - visibleRecordGroups, - viewGroupFieldMetadataItem, - }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts new file mode 100644 index 000000000..d84e393a0 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts @@ -0,0 +1,83 @@ +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useContext } from 'react'; +import { useRecoilCallback } from 'recoil'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; +import { isDefined } from '~/utils/isDefined'; + +export const useSetRecordGroup = (viewId?: string) => { + const { objectMetadataItem } = useContext(RecordIndexRootPropsContext); + + const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( + recordGroupIdsComponentState, + viewId, + ); + + const recordGroupFieldMetadataState = useRecoilComponentCallbackStateV2( + recordGroupFieldMetadataComponentState, + viewId, + ); + + return useRecoilCallback( + ({ snapshot, set }) => + (recordGroups: RecordGroupDefinition[]) => { + if (recordGroups.length === 0) { + return; + } + + const currentRecordGroupId = getSnapshotValue( + snapshot, + recordIndexRecordGroupIdsState, + ); + const fieldMetadataId = recordGroups[0].fieldMetadataId; + const fieldMetadata = objectMetadataItem.fields.find( + (field) => field.id === fieldMetadataId, + ); + const currentFieldMetadata = getSnapshotValue( + snapshot, + recordGroupFieldMetadataState, + ); + + // Set the field metadata linked to the record groups + if ( + isDefined(fieldMetadata) && + !isDeeplyEqual(fieldMetadata, currentFieldMetadata) + ) { + set(recordGroupFieldMetadataState, fieldMetadata); + } + + // Set the record groups by id + recordGroups.forEach((recordGroup) => { + const existingRecordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroup.id), + ); + + if (isDeeplyEqual(existingRecordGroup, recordGroup)) { + return; + } + + set(recordGroupDefinitionFamilyState(recordGroup.id), recordGroup); + }); + + const recordGroupIds = recordGroups.map(({ id }) => id); + + if (isDeeplyEqual(currentRecordGroupId, recordGroupIds)) { + return; + } + + // Set the record group ids + set(recordIndexRecordGroupIdsState, recordGroupIds); + }, + [ + objectMetadataItem.fields, + recordGroupFieldMetadataState, + recordIndexRecordGroupIdsState, + ], + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionFamilyState.ts new file mode 100644 index 000000000..80cf70f68 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionFamilyState.ts @@ -0,0 +1,10 @@ +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { atomFamily } from 'recoil'; + +export const recordGroupDefinitionFamilyState = atomFamily< + RecordGroupDefinition | undefined, + RecordGroupDefinition['id'] +>({ + key: 'recordGroupDefinitionFamilyState', + default: undefined, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupFieldMetadataComponentState.ts b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupFieldMetadataComponentState.ts new file mode 100644 index 000000000..144b47a04 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupFieldMetadataComponentState.ts @@ -0,0 +1,11 @@ +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const recordGroupFieldMetadataComponentState = createComponentStateV2< + FieldMetadataItem | undefined +>({ + key: 'recordGroupFieldMetadataComponentState', + defaultValue: undefined, + componentInstanceContext: ViewComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupIdsComponentState.ts similarity index 72% rename from packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionsComponentState.ts rename to packages/twenty-front/src/modules/object-record/record-group/states/recordGroupIdsComponentState.ts index 56ec80fcc..6665fb6e4 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionsComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/states/recordGroupIdsComponentState.ts @@ -2,10 +2,10 @@ import { RecordGroupDefinition } from '@/object-record/record-group/types/Record import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; -export const recordGroupDefinitionsComponentState = createComponentStateV2< - RecordGroupDefinition[] +export const recordGroupIdsComponentState = createComponentStateV2< + RecordGroupDefinition['id'][] >({ - key: 'recordGroupDefinitionsComponentState', + key: 'recordGroupIdsComponentState', defaultValue: [], componentInstanceContext: ViewComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector.ts similarity index 52% rename from packages/twenty-front/src/modules/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector.ts rename to packages/twenty-front/src/modules/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector.ts index bccab902c..760324010 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector.ts @@ -1,21 +1,21 @@ -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; -export const hasRecordGroupDefinitionsComponentSelector = +export const hasRecordGroupsComponentSelector = createComponentSelectorV2({ - key: 'hasRecordGroupDefinitionsComponentSelector', + key: 'hasRecordGroupsComponentSelector', componentInstanceContext: ViewComponentInstanceContext, get: ({ instanceId }) => ({ get }) => { - const recordGroupDefinitions = get( - recordGroupDefinitionsComponentState.atomFamily({ + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ instanceId, }), ); - return recordGroupDefinitions.length > 0; + return recordGroupIds.length > 0; }, }); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector.ts new file mode 100644 index 000000000..4c57886cb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector.ts @@ -0,0 +1,34 @@ +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; + +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; +import { isDefined } from '~/utils/isDefined'; + +export const hiddenRecordGroupIdsComponentSelector = createComponentSelectorV2< + string[] +>({ + key: 'hiddenRecordGroupIdsComponentSelector', + componentInstanceContext: ViewComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ + instanceId, + }), + ); + + return recordGroupIds.filter((recordGroupId) => { + const recordGroupDefinition = get( + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroupDefinition)) { + return false; + } + + return !recordGroupDefinition.isVisible; + }); + }, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector.ts new file mode 100644 index 000000000..ce0e0432e --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector.ts @@ -0,0 +1,37 @@ +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; + +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; +import { isDefined } from '~/utils/isDefined'; + +export const recordGroupDefinitionsComponentSelector = + createComponentSelectorV2({ + key: 'recordGroupDefinitionsComponentSelector', + componentInstanceContext: ViewComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ + instanceId, + }), + ); + + return recordGroupIds.reduce( + (acc, recordGroupId) => { + const recordGroupDefinition = get( + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroupDefinition)) { + return acc; + } + + return [...acc, recordGroupDefinition]; + }, + [], + ); + }, + }); diff --git a/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts new file mode 100644 index 000000000..4c2332ebb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts @@ -0,0 +1,63 @@ +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { RecordGroupSort } from '@/object-record/record-group/types/RecordGroupSort'; +import { sortedInsert } from '@/object-record/record-group/utils/sortedInsert'; +import { recordIndexRecordGroupSortComponentState } from '@/object-record/record-index/states/recordIndexRecordGroupSortComponentState'; + +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; +import { isDefined } from '~/utils/isDefined'; + +export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< + string[] +>({ + key: 'visibleRecordGroupIdsComponentSelector', + componentInstanceContext: ViewComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const recordGroupSort = get( + recordIndexRecordGroupSortComponentState.atomFamily({ + instanceId, + }), + ); + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ + instanceId, + }), + ); + + const result: RecordGroupDefinition[] = []; + + const comparator = ( + a: RecordGroupDefinition, + b: RecordGroupDefinition, + ) => { + switch (recordGroupSort) { + case RecordGroupSort.Alphabetical: + return a.title.localeCompare(b.title); + case RecordGroupSort.ReverseAlphabetical: + return b.title.localeCompare(a.title); + case RecordGroupSort.Manual: + default: + return a.position - b.position; + } + }; + + for (const recordGroupId of recordGroupIds) { + const recordGroupDefinition = get( + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if ( + isDefined(recordGroupDefinition) && + recordGroupDefinition.isVisible + ) { + sortedInsert(result, recordGroupDefinition, comparator); + } + } + + return result.map(({ id }) => id); + }, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-group/utils/sortRecordGroupDefinitions.ts b/packages/twenty-front/src/modules/object-record/record-group/utils/sortRecordGroupDefinitions.ts index 45c3a68b6..afe2aa41a 100644 --- a/packages/twenty-front/src/modules/object-record/record-group/utils/sortRecordGroupDefinitions.ts +++ b/packages/twenty-front/src/modules/object-record/record-group/utils/sortRecordGroupDefinitions.ts @@ -5,7 +5,7 @@ export const sortRecordGroupDefinitions = ( recordGroupDefinitions: RecordGroupDefinition[], recordGroupSort: RecordGroupSort, ) => { - const visibleGroups = recordGroupDefinitions.filter( + const visibleRecordGroups = recordGroupDefinitions.filter( (boardGroup) => boardGroup.isVisible, ); @@ -17,15 +17,15 @@ export const sortRecordGroupDefinitions = ( switch (recordGroupSort) { case RecordGroupSort.Alphabetical: - return visibleGroups.sort((a, b) => + return visibleRecordGroups.sort((a, b) => compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase()), ); case RecordGroupSort.ReverseAlphabetical: - return visibleGroups.sort((a, b) => + return visibleRecordGroups.sort((a, b) => compareAlphabetical(a.title.toLowerCase(), b.title.toLowerCase(), true), ); case RecordGroupSort.Manual: default: - return visibleGroups.sort((a, b) => a.position - b.position); + return visibleRecordGroups.sort((a, b) => a.position - b.position); } }; diff --git a/packages/twenty-front/src/modules/object-record/record-group/utils/sortedInsert.ts b/packages/twenty-front/src/modules/object-record/record-group/utils/sortedInsert.ts new file mode 100644 index 000000000..5a7dd1027 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-group/utils/sortedInsert.ts @@ -0,0 +1,20 @@ +export const sortedInsert = ( + array: T[], + item: T, + comparator: (a: T, b: T) => number, +) => { + let low = 0; + let high = array.length; + + while (low < high) { + const mid = Math.floor((low + high) / 2); + + if (comparator(item, array[mid]) < 0) { + high = mid; + } else { + low = mid + 1; + } + } + + array.splice(low, 0, item); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx index 6f6a2e2be..bde4108f5 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect.tsx @@ -5,7 +5,7 @@ import { isRecordBoardFetchingRecordsByColumnFamilyState } from '@/object-record import { recordBoardShouldFetchMoreInColumnComponentFamilyState } from '@/object-record/record-board/states/recordBoardShouldFetchMoreInColumnComponentFamilyState'; import { useLoadRecordIndexBoardColumn } from '@/object-record/record-index/hooks/useLoadRecordIndexBoardColumn'; import { isRecordIndexBoardColumnLoadingFamilyState } from '@/object-record/states/isRecordBoardColumnLoadingFamilyState'; -import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; +import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2'; export const RecordIndexBoardColumnLoaderEffect = ({ objectNameSingular, @@ -18,20 +18,14 @@ export const RecordIndexBoardColumnLoaderEffect = ({ boardFieldMetadataId: string | null; columnId: string; }) => { - const [shouldFetchMore, setShouldFetchMore] = useRecoilState( - recordBoardShouldFetchMoreInColumnComponentFamilyState({ - scopeId: getScopeIdFromComponentId(recordBoardId), - familyKey: columnId, - }), + const [shouldFetchMore, setShouldFetchMore] = useRecoilComponentFamilyStateV2( + recordBoardShouldFetchMoreInColumnComponentFamilyState, + columnId, + recordBoardId, ); const [loadingRecordsForThisColumn, setLoadingRecordsForThisColumn] = - useRecoilState( - isRecordBoardFetchingRecordsByColumnFamilyState({ - scopeId: getScopeIdFromComponentId(recordBoardId), - familyKey: { columnId }, - }), - ); + useRecoilState(isRecordBoardFetchingRecordsByColumnFamilyState(columnId)); const { fetchMoreRecords, loading, records, hasNextPage } = useLoadRecordIndexBoardColumn({ diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx index 194580587..e8bd37d05 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoader.tsx @@ -1,9 +1,10 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; import { RecordIndexBoardColumnLoaderEffect } from '@/object-record/record-index/components/RecordIndexBoardColumnLoaderEffect'; import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; type RecordIndexBoardDataLoaderProps = { objectNameSingular: string; @@ -18,6 +19,10 @@ export const RecordIndexBoardDataLoader = ({ objectNameSingular, }); + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); + const recordIndexKanbanFieldMetadataId = useRecoilValue( recordIndexKanbanFieldMetadataIdState, ); @@ -26,18 +31,14 @@ export const RecordIndexBoardDataLoader = ({ (field) => field.id === recordIndexKanbanFieldMetadataId, ); - const { columnIdsState } = useRecordBoardStates(recordBoardId); - - const columnIds = useRecoilValue(columnIdsState); - return ( <> - {columnIds.map((columnId, index) => ( + {visibleRecordGroupIds.map((recordGroupId, index) => ( ))} diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx index 02834d1e7..81f64f3f6 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx @@ -1,101 +1,52 @@ import { useEffect } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; +import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; +import { recordBoardSelectedRecordIdsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardSelectedRecordIdsComponentSelector'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; -import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; -import { isDefined } from '~/utils/isDefined'; type RecordIndexBoardDataLoaderEffectProps = { - objectNameSingular: string; recordBoardId: string; }; export const RecordIndexBoardDataLoaderEffect = ({ - objectNameSingular, recordBoardId, }: RecordIndexBoardDataLoaderEffectProps) => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular, - }); - const recordIndexFieldDefinitions = useRecoilValue( recordIndexFieldDefinitionsState, ); - const recordGroupDefinitions = useRecoilComponentValueV2( - recordGroupDefinitionsComponentState, - ); - - const recordIndexKanbanFieldMetadataId = useRecoilValue( - recordIndexKanbanFieldMetadataIdState, - ); - const recordIndexIsCompactModeActive = useRecoilValue( recordIndexIsCompactModeActiveState, ); - const { isCompactModeActiveState } = useRecordBoard(recordBoardId); + const setRecordBoardFieldDefinitions = useSetRecoilComponentStateV2( + recordBoardFieldDefinitionsComponentState, + recordBoardId, + ); - const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState); + const selectedRecordIds = useRecoilComponentValueV2( + recordBoardSelectedRecordIdsComponentSelector, + recordBoardId, + ); + + const setIsCompactModeActive = useSetRecoilComponentStateV2( + isRecordBoardCompactModeActiveComponentState, + recordBoardId, + ); useEffect(() => { setIsCompactModeActive(recordIndexIsCompactModeActive); }, [recordIndexIsCompactModeActive, setIsCompactModeActive]); - const { - setColumns, - setObjectSingularName, - selectedRecordIdsSelector, - setFieldDefinitions, - setKanbanFieldMetadataName, - } = useRecordBoard(recordBoardId); - useEffect(() => { - setFieldDefinitions(recordIndexFieldDefinitions); - }, [recordIndexFieldDefinitions, setFieldDefinitions]); - - useEffect(() => { - setObjectSingularName(objectNameSingular); - }, [objectNameSingular, setObjectSingularName]); - - useEffect(() => { - setColumns(recordGroupDefinitions); - }, [recordGroupDefinitions, setColumns]); - - // TODO: Remove this duplicate useEffect by ensuring it's not here because - // We want it to be triggered by a change of objectMetadataItem, which would be an anti-pattern - // As it is an unnecessary dependency - useEffect(() => { - setFieldDefinitions(recordIndexFieldDefinitions); - }, [objectMetadataItem, setFieldDefinitions, recordIndexFieldDefinitions]); - - useEffect(() => { - if (isDefined(recordIndexKanbanFieldMetadataId)) { - const kanbanFieldMetadataName = objectMetadataItem?.fields.find( - (field) => - field.type === FieldMetadataType.Select && - field.id === recordIndexKanbanFieldMetadataId, - )?.name; - - if (isDefined(kanbanFieldMetadataName)) { - setKanbanFieldMetadataName(kanbanFieldMetadataName); - } - } - }, [ - objectMetadataItem, - recordIndexKanbanFieldMetadataId, - setKanbanFieldMetadataName, - ]); - - const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector()); + setRecordBoardFieldDefinitions(recordIndexFieldDefinitions); + }, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]); const setContextStoreTargetedRecords = useSetRecoilComponentStateV2( contextStoreTargetedRecordsRuleComponentState, diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx index feda1b8b9..0da7a7172 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexContainer.tsx @@ -24,11 +24,9 @@ import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/compone import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { useSetRecordGroup } from '@/object-record/record-group/hooks/useSetRecordGroup'; import { RecordIndexFiltersToContextStoreEffect } from '@/object-record/record-index/components/RecordIndexFiltersToContextStoreEffect'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; -import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { ViewBar } from '@/views/components/ViewBar'; import { ViewField } from '@/views/types/ViewField'; @@ -38,7 +36,7 @@ import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToC import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; import { mapViewGroupsToRecordGroupDefinitions } from '@/views/utils/mapViewGroupsToRecordGroupDefinitions'; import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; -import { useContext } from 'react'; +import { useCallback, useContext } from 'react'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; const StyledContainer = styled.div` @@ -68,9 +66,7 @@ export const RecordIndexContainer = () => { objectNameSingular, } = useContext(RecordIndexRootPropsContext); - const recordGroupDefinitionsCallbackState = useRecoilComponentCallbackStateV2( - recordGroupDefinitionsComponentState, - ); + const setRecordGroup = useSetRecordGroup(recordIndexId); const { columnDefinitions, filterDefinitions, sortDefinitions } = useColumnDefinitionsFromFieldMetadata(objectMetadataItem); @@ -96,8 +92,6 @@ export const RecordIndexContainer = () => { recordTableId: recordIndexId, }); - const { setColumns } = useRecordBoard(recordIndexId); - const onViewFieldsChange = useRecoilCallback( ({ set, snapshot }) => (viewFields: ViewField[]) => { @@ -124,30 +118,16 @@ export const RecordIndexContainer = () => { [columnDefinitions, setTableColumns], ); - const onViewGroupsChange = useRecoilCallback( - ({ set, snapshot }) => - (viewGroups: ViewGroup[]) => { - const newGroupDefinitions = mapViewGroupsToRecordGroupDefinitions({ - objectMetadataItem, - viewGroups, - }); + const onViewGroupsChange = useCallback( + (viewGroups: ViewGroup[]) => { + const newGroupDefinitions = mapViewGroupsToRecordGroupDefinitions({ + objectMetadataItem, + viewGroups, + }); - setColumns(newGroupDefinitions); - - const existingRecordIndexGroupDefinitions = snapshot - .getLoadable(recordGroupDefinitionsCallbackState) - .getValue(); - - if ( - !isDeeplyEqual( - existingRecordIndexGroupDefinitions, - newGroupDefinitions, - ) - ) { - set(recordGroupDefinitionsCallbackState, newGroupDefinitions); - } - }, - [objectMetadataItem, recordGroupDefinitionsCallbackState, setColumns], + setRecordGroup(newGroupDefinitions); + }, + [objectMetadataItem, setRecordGroup], ); const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2( @@ -229,10 +209,7 @@ export const RecordIndexContainer = () => { objectNameSingular={objectNameSingular} recordBoardId={recordIndexId} /> - + )} diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddButton.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddButton.tsx index 65c8b1303..ace9040ae 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddButton.tsx @@ -1,8 +1,9 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; import { useIsOpportunitiesCompanyFieldDisabled } from '@/object-record/record-board/record-board-column/hooks/useIsOpportunitiesCompanyFieldDisabled'; +import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { RecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; @@ -11,6 +12,7 @@ import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import styled from '@emotion/styled'; import { useCallback, useContext } from 'react'; import { useRecoilValue } from 'recoil'; @@ -32,6 +34,10 @@ export const RecordIndexPageKanbanAddButton = () => { ); const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular }); + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); + const recordIndexKanbanFieldMetadataId = useRecoilValue( recordIndexKanbanFieldMetadataIdState, ); @@ -42,12 +48,11 @@ export const RecordIndexPageKanbanAddButton = () => { const isOpportunity = objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity; - const { columnIdsState, visibleFieldDefinitionsState } = - useRecordBoardStates(recordIndexId); - const columnIds = useRecoilValue(columnIdsState); - const visibleFieldDefinitions = useRecoilValue( - visibleFieldDefinitionsState(), + const visibleFieldDefinitions = useRecoilComponentValueV2( + recordBoardVisibleFieldDefinitionsComponentSelector, + recordIndexId, ); + const labelIdentifierField = visibleFieldDefinitions.find( (field) => field.isLabelIdentifier, ); @@ -101,11 +106,10 @@ export const RecordIndexPageKanbanAddButton = () => { dropdownComponents={ - {columnIds.map((columnId) => ( + {visibleRecordGroupIds.map((recordGroupId) => ( ))} diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx index effa5ce3a..c209a6b0e 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexPageKanbanAddMenuItem.tsx @@ -1,7 +1,9 @@ +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { useRecordIndexPageKanbanAddMenuItem } from '@/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem'; import styled from '@emotion/styled'; +import { useRecoilValue } from 'recoil'; import { MenuItem, Tag } from 'twenty-ui'; +import { isDefined } from '~/utils/isDefined'; const StyledMenuItem = styled(MenuItem)` width: calc(100% - 2 * var(--horizontal-padding)); @@ -9,20 +11,18 @@ const StyledMenuItem = styled(MenuItem)` type RecordIndexPageKanbanAddMenuItemProps = { columnId: string; - recordIndexId: string; onItemClick: (columnDefinition: any) => void; }; export const RecordIndexPageKanbanAddMenuItem = ({ columnId, - recordIndexId, onItemClick, }: RecordIndexPageKanbanAddMenuItemProps) => { - const { columnDefinition } = useRecordIndexPageKanbanAddMenuItem( - recordIndexId, - columnId, + const recordGroupDefinition = useRecoilValue( + recordGroupDefinitionFamilyState(columnId), ); - if (!columnDefinition) { + + if (!isDefined(recordGroupDefinition)) { return null; } @@ -31,24 +31,24 @@ export const RecordIndexPageKanbanAddMenuItem = ({ text={ } - onClick={() => onItemClick(columnDefinition)} + onClick={() => onItemClick(recordGroupDefinition)} /> ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts index e1bc40ca0..94042f4e0 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/__tests__/useExportFetchRecords.test.ts @@ -8,14 +8,12 @@ import { import { PERSON_FRAGMENT_WITH_DEPTH_ZERO_RELATIONS } from '@/object-record/hooks/__mocks__/personFragments'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; -import { recordBoardKanbanFieldMetadataNameComponentState } from '@/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState'; -import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { ViewType } from '@/views/types/ViewType'; import { MockedResponse } from '@apollo/client/testing'; import { expect } from '@storybook/test'; import gql from 'graphql-tag'; -import { useRecoilValue } from 'recoil'; import { getJestMetadataAndApolloMocksAndContextStoreWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndContextStoreWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; @@ -232,10 +230,12 @@ describe('useRecordData', () => { const callback = jest.fn(); const { result } = renderHook( () => { - const kanbanFieldNameState = extractComponentState( - recordBoardKanbanFieldMetadataNameComponentState, - recordIndexId, - ); + const [recordGroupFieldMetadata, setRecordGroupFieldMetadata] = + useRecoilComponentStateV2( + recordGroupFieldMetadataComponentState, + recordIndexId, + ); + return { tableData: useExportFetchRecords({ recordIndexId, @@ -246,8 +246,8 @@ describe('useRecordData', () => { delayMs: 0, viewType: ViewType.Kanban, }), - useRecordBoardHook: useRecordBoard(recordIndexId), - kanbanFieldName: useRecoilValue(kanbanFieldNameState), + kanbanFieldName: recordGroupFieldMetadata?.name, + setRecordGroupFieldMetadata, kanbanData: useObjectOptionsForBoard({ objectNameSingular: objectMetadataItem.nameSingular, recordBoardId: recordIndexId, @@ -269,9 +269,7 @@ describe('useRecordData', () => { ); await act(async () => { - result.current.useRecordBoardHook.setKanbanFieldMetadataName( - updatedAtFieldMetadataItem?.name, - ); + result.current.setRecordGroupFieldMetadata(updatedAtFieldMetadataItem); }); await act(async () => { @@ -322,10 +320,12 @@ describe('useRecordData', () => { const callback = jest.fn(); const { result } = renderHook( () => { - const kanbanFieldNameState = extractComponentState( - recordBoardKanbanFieldMetadataNameComponentState, - recordIndexId, - ); + const [recordGroupFieldMetadata, setRecordGroupFieldMetadata] = + useRecoilComponentStateV2( + recordGroupFieldMetadataComponentState, + recordIndexId, + ); + return { tableData: useExportFetchRecords({ recordIndexId, @@ -336,8 +336,9 @@ describe('useRecordData', () => { delayMs: 0, viewType: ViewType.Table, }), - setKanbanFieldName: useRecordBoard(recordIndexId), - kanbanFieldName: useRecoilValue(kanbanFieldNameState), + objectMetadataItem, + kanbanFieldName: recordGroupFieldMetadata?.name, + setRecordGroupFieldMetadata, kanbanData: useObjectOptionsForBoard({ objectNameSingular: objectMetadataItem.nameSingular, recordBoardId: recordIndexId, @@ -351,9 +352,14 @@ describe('useRecordData', () => { ); await act(async () => { - result.current.setKanbanFieldName.setKanbanFieldMetadataName( - result.current.kanbanData.hiddenBoardFields[0].metadata.fieldName, - ); + const fieldMetadataItem = + result.current.objectMetadataItem?.fields.find( + (fieldMetadata) => + fieldMetadata.id === + result.current.kanbanData.hiddenBoardFields[0].fieldMetadataId, + ); + + result.current.setRecordGroupFieldMetadata(fieldMetadataItem); }); await act(async () => { diff --git a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts index bcc82de30..523762226 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/export/hooks/useExportFetchRecords.ts @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import { useRecoilValue } from 'recoil'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; @@ -13,7 +12,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords'; import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/object-options-dropdown/constants/ExportTableDataDefaultPageSize'; import { useObjectOptionsForBoard } from '@/object-record/object-options-dropdown/hooks/useObjectOptionsForBoard'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -68,10 +67,13 @@ export const useExportFetchRecords = ({ viewBarId: recordIndexId, }); - const { kanbanFieldMetadataNameState } = useRecordBoardStates(recordIndexId); - const kanbanFieldMetadataName = useRecoilValue(kanbanFieldMetadataNameState); + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + recordIndexId, + ); + const hiddenKanbanFieldColumn = hiddenBoardFields.find( - (column) => column.metadata.fieldName === kanbanFieldMetadataName, + (column) => column.metadata.fieldName === recordGroupFieldMetadata?.name, ); const columns = useRecoilComponentValueV2( visibleTableColumnsComponentSelector, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts index 7b241909d..fee8cc97a 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoard.ts @@ -1,12 +1,13 @@ import { useEffect } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { useSetRecordBoardRecordIds } from '@/object-record/record-board/hooks/useSetRecordBoardRecordIds'; +import { isRecordBoardCompactModeActiveComponentState } from '@/object-record/record-board/states/isRecordBoardCompactModeActiveComponentState'; +import { recordBoardFieldDefinitionsComponentState } from '@/object-record/record-board/states/recordBoardFieldDefinitionsComponentState'; import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; @@ -14,7 +15,7 @@ import { recordIndexIsCompactModeActiveState } from '@/object-record/record-inde import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; import { recordIndexViewFilterGroupsState } from '@/object-record/record-index/states/recordIndexViewFilterGroupsState'; import { useUpsertRecordsInStore } from '@/object-record/record-store/hooks/useUpsertRecordsInStore'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView'; type UseLoadRecordIndexBoardProps = { @@ -31,33 +32,28 @@ export const useLoadRecordIndexBoard = ({ const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); - const { - setRecordIds: setRecordIdsInBoard, - setFieldDefinitions, - setColumns, - isCompactModeActiveState, - } = useRecordBoard(recordBoardId); + + const setRecordBoardFieldDefinitions = useSetRecoilComponentStateV2( + recordBoardFieldDefinitionsComponentState, + recordBoardId, + ); + + const { setRecordIds: setRecordIdsInBoard } = + useSetRecordBoardRecordIds(recordBoardId); + const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); const recordIndexFieldDefinitions = useRecoilValue( recordIndexFieldDefinitionsState, ); useEffect(() => { - setFieldDefinitions(recordIndexFieldDefinitions); - }, [recordIndexFieldDefinitions, setFieldDefinitions]); + setRecordBoardFieldDefinitions(recordIndexFieldDefinitions); + }, [recordIndexFieldDefinitions, setRecordBoardFieldDefinitions]); const recordIndexViewFilterGroups = useRecoilValue( recordIndexViewFilterGroupsState, ); - const recordGroupDefinitions = useRecoilComponentValueV2( - recordGroupDefinitionsComponentState, - ); - - useEffect(() => { - setColumns(recordGroupDefinitions); - }, [recordGroupDefinitions, setColumns]); - const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexSorts = useRecoilValue(recordIndexSortsState); const requestFilters = computeViewRecordGqlOperationFilter( @@ -92,7 +88,10 @@ export const useLoadRecordIndexBoard = ({ const { setRecordCountInCurrentView } = useSetRecordCountInCurrentView(viewBarId); - const setIsCompactModeActive = useSetRecoilState(isCompactModeActiveState); + const setIsCompactModeActive = useSetRecoilComponentStateV2( + isRecordBoardCompactModeActiveComponentState, + recordBoardId, + ); useEffect(() => { setRecordIdsInBoard(records); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts index 99a522033..273811805 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexBoardColumn.ts @@ -4,9 +4,9 @@ import { useRecoilValue } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard'; +import { useSetRecordIdsForColumn } from '@/object-record/record-board/hooks/useSetRecordIdsForColumn'; import { computeViewRecordGqlOperationFilter } from '@/object-record/record-filter/utils/computeViewRecordGqlOperationFilter'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; import { useRecordBoardRecordGqlFields } from '@/object-record/record-index/hooks/useRecordBoardRecordGqlFields'; import { recordIndexFiltersState } from '@/object-record/record-index/states/recordIndexFiltersState'; import { recordIndexSortsState } from '@/object-record/record-index/states/recordIndexSortsState'; @@ -30,16 +30,18 @@ export const useLoadRecordIndexBoardColumn = ({ const { objectMetadataItem } = useObjectMetadataItem({ objectNameSingular, }); - const { setRecordIdsForColumn } = useRecordBoard(recordBoardId); - const { columnsFamilySelector } = useRecordBoardStates(recordBoardId); + const { setRecordIdsForColumn } = useSetRecordIdsForColumn(recordBoardId); const { upsertRecords: upsertRecordsInStore } = useUpsertRecordsInStore(); + const recordGroupDefinition = useRecoilValue( + recordGroupDefinitionFamilyState(columnId), + ); + const recordIndexViewFilterGroups = useRecoilValue( recordIndexViewFilterGroupsState, ); const recordIndexFilters = useRecoilValue(recordIndexFiltersState); const recordIndexSorts = useRecoilValue(recordIndexSortsState); - const columnDefinition = useRecoilValue(columnsFamilySelector(columnId)); const requestFilters = computeViewRecordGqlOperationFilter( recordIndexFilters, @@ -60,9 +62,9 @@ export const useLoadRecordIndexBoardColumn = ({ const filter = { ...requestFilters, [recordIndexKanbanFieldMetadataItem?.name ?? '']: isDefined( - columnDefinition?.value, + recordGroupDefinition?.value, ) - ? { in: [columnDefinition?.value] } + ? { in: [recordGroupDefinition?.value] } : { is: 'NULL' }, }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts index 8162bb7df..d705ef14b 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useLoadRecordIndexTable.ts @@ -27,8 +27,7 @@ export const useFindManyParams = ( objectNameSingular, }); - const currentRecordGroupDefinition = - useCurrentRecordGroupDefinition(recordTableId); + const currentRecordGroupDefinition = useCurrentRecordGroupDefinition(); const tableViewFilterGroups = useRecoilComponentValueV2( tableViewFilterGroupsComponentState, diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts index aef238c2b..4386c2922 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts @@ -1,9 +1,9 @@ -import { useRecoilValue } from 'recoil'; - import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields'; import { hasPositionField } from '@/object-metadata/utils/hasPositionField'; -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; +import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; +import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { isDefined } from '~/utils/isDefined'; export const useRecordBoardRecordGqlFields = ({ @@ -13,15 +13,17 @@ export const useRecordBoardRecordGqlFields = ({ recordBoardId: string; objectMetadataItem: ObjectMetadataItem; }) => { - const { kanbanFieldMetadataNameState, visibleFieldDefinitionsState } = - useRecordBoardStates(recordBoardId); + const visibleFieldDefinitions = useRecoilComponentValueV2( + recordBoardVisibleFieldDefinitionsComponentSelector, + recordBoardId, + ); const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } = getObjectMetadataIdentifierFields({ objectMetadataItem }); - const kanbanFieldMetadataName = useRecoilValue(kanbanFieldMetadataNameState); - const visibleFieldDefinitions = useRecoilValue( - visibleFieldDefinitionsState(), + const recordGroupFieldMetadata = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + recordBoardId, ); const identifierQueryFields: Record = {}; @@ -59,8 +61,8 @@ export const useRecordBoardRecordGqlFields = ({ }, }; - if (isDefined(kanbanFieldMetadataName)) { - recordGqlFields[kanbanFieldMetadataName] = true; + if (isDefined(recordGroupFieldMetadata?.name)) { + recordGqlFields[recordGroupFieldMetadata.name] = true; } return recordGqlFields; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem.ts deleted file mode 100644 index 8e5604cb0..000000000 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates'; -import { useRecoilValue } from 'recoil'; - -export const useRecordIndexPageKanbanAddMenuItem = ( - recordIndexId: string, - columnId: string, -) => { - const { columnsFamilySelector } = useRecordBoardStates(recordIndexId); - const columnDefinition = useRecoilValue(columnsFamilySelector(columnId)); - - return { columnDefinition }; -}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexAllRowIdsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexAllRowIdsComponentState.ts new file mode 100644 index 000000000..7e163fd08 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexAllRowIdsComponentState.ts @@ -0,0 +1,10 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const recordIndexAllRowIdsComponentState = createComponentStateV2< + string[] +>({ + key: 'recordIndexAllRowIdsComponentState', + defaultValue: [], + componentInstanceContext: ViewComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts similarity index 50% rename from packages/twenty-front/src/modules/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState.ts rename to packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts index 395f7f185..fe35021c6 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts @@ -1,10 +1,10 @@ import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; -export const tableRowIdsByGroupComponentFamilyState = +export const recordIndexRowIdsByGroupComponentFamilyState = createComponentFamilyStateV2({ - key: 'tableRowIdsByGroupComponentFamilyState', + key: 'recordIndexRowIdsByGroupComponentFamilyState', defaultValue: [], - componentInstanceContext: RecordTableComponentInstanceContext, + componentInstanceContext: ViewComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx index ac2fd976d..834761638 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTable.tsx @@ -1,7 +1,8 @@ import styled from '@emotion/styled'; import { isNonEmptyString, isNull } from '@sniptt/guards'; -import { hasRecordGroupDefinitionsComponentSelector } from '@/object-record/record-group/states/hasRecordGroupDefinitionsComponentSelector'; +import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableComponentInstance } from '@/object-record/record-table/components/RecordTableComponentInstance'; import { RecordTableContextProvider } from '@/object-record/record-table/components/RecordTableContextProvider'; import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect'; @@ -16,7 +17,6 @@ import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect'; import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -53,8 +53,8 @@ export const RecordTable = ({ recordTableId, ); - const tableRowIds = useRecoilComponentValueV2( - tableAllRowIdsComponentState, + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, recordTableId, ); @@ -64,13 +64,13 @@ export const RecordTable = ({ ); const hasRecordGroups = useRecoilComponentValueV2( - hasRecordGroupDefinitionsComponentSelector, + hasRecordGroupsComponentSelector, recordTableId, ); const recordTableIsEmpty = !isRecordTableInitialLoading && - tableRowIds.length === 0 && + allRowIds.length === 0 && isNull(pendingRecordId); const { resetTableRowSelection, setRowSelected } = useRecordTable({ @@ -109,9 +109,7 @@ export const RecordTable = ({ {!hasRecordGroups ? ( ) : ( - + )} diff --git a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupRows.tsx b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupRows.tsx index f36fa08ae..7febddcdf 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupRows.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/components/RecordTableNoRecordGroupRows.tsx @@ -1,14 +1,16 @@ +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableBodyFetchMoreLoader } from '@/object-record/record-table/record-table-body/components/RecordTableBodyFetchMoreLoader'; import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableNoRecordGroupRows = () => { - const rowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, + ); return ( <> - {rowIds.map((recordId, rowIndex) => { + {allRowIds.map((recordId, rowIndex) => { return ( { const recordGroupId = useCurrentRecordGroupId(); - const allRowIds = useRecoilComponentValueV2(tableAllRowIdsComponentState); + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, + ); const recordGroupRowIds = useRecoilComponentFamilyValueV2( - tableRowIdsByGroupComponentFamilyState, + recordIndexRowIdsByGroupComponentFamilyState, recordGroupId, ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyHandler.tsx b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyHandler.tsx index 7b2809000..debedb73f 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyHandler.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/empty-state/components/RecordTableEmptyHandler.tsx @@ -1,9 +1,9 @@ import { isNull } from '@sniptt/guards'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; type RecordTableEmptyHandlerProps = { @@ -20,8 +20,8 @@ export const RecordTableEmptyHandler = ({ recordTableId, ); - const tableRowIds = useRecoilComponentValueV2( - tableAllRowIdsComponentState, + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, recordTableId, ); @@ -32,7 +32,7 @@ export const RecordTableEmptyHandler = ({ const recordTableIsEmpty = !isRecordTableInitialLoading && - tableRowIds.length === 0 && + allRowIds.length === 0 && isNull(pendingRecordId); if (recordTableIsEmpty) { diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useResetTableRowSelection.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useResetTableRowSelection.ts index 82b51a2e1..409c30a46 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useResetTableRowSelection.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useResetTableRowSelection.ts @@ -2,10 +2,10 @@ import { useRecoilCallback } from 'recoil'; import { getActionMenuDropdownIdFromActionMenuId } from '@/action-menu/utils/getActionMenuDropdownIdFromActionMenuId'; import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; @@ -18,8 +18,8 @@ export const useResetTableRowSelection = (recordTableId?: string) => { recordTableId, ); - const tableAllRowIdsState = useRecoilComponentCallbackStateV2( - tableAllRowIdsComponentState, + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, recordTableIdFromContext, ); @@ -43,9 +43,9 @@ export const useResetTableRowSelection = (recordTableId?: string) => { return useRecoilCallback( ({ set, snapshot }) => () => { - const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); + const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); - for (const rowId of tableRowIds) { + for (const rowId of allRowIds) { set(isRowSelectedFamilyState(rowId), false); } @@ -54,7 +54,7 @@ export const useResetTableRowSelection = (recordTableId?: string) => { set(isActionMenuDropdownOpenState, false); }, [ - tableAllRowIdsState, + recordIndexAllRowIdsState, hasUserSelectedAllRowsState, isActionMenuDropdownOpenState, isRowSelectedFamilyState, diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSelectAllRows.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSelectAllRows.ts index 24d54fb1b..f715435c0 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSelectAllRows.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSelectAllRows.ts @@ -1,8 +1,8 @@ import { useRecoilCallback } from 'recoil'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -15,8 +15,8 @@ export const useSelectAllRows = (recordTableId?: string) => { isRowSelectedComponentFamilyState, recordTableId, ); - const tableAllRowIdsState = useRecoilComponentCallbackStateV2( - tableAllRowIdsComponentState, + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, recordTableId, ); @@ -28,24 +28,24 @@ export const useSelectAllRows = (recordTableId?: string) => { allRowsSelectedStatusSelector, ); - const tableRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); + const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); if ( allRowsSelectedStatus === 'none' || allRowsSelectedStatus === 'some' ) { - for (const rowId of tableRowIds) { + for (const rowId of allRowIds) { set(isRowSelectedFamilyState(rowId), true); } } else { - for (const rowId of tableRowIds) { + for (const rowId of allRowIds) { set(isRowSelectedFamilyState(rowId), false); } } }, [ allRowsSelectedStatusSelector, - tableAllRowIdsState, + recordIndexAllRowIdsState, isRowSelectedFamilyState, ], ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts index 7a35ef87a..dcfa9e6f5 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/internal/useSetRecordTableData.ts @@ -1,11 +1,11 @@ import { useRecoilCallback } from 'recoil'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { hasUserSelectedAllRowsComponentState } from '@/object-record/record-table/record-table-row/states/hasUserSelectedAllRowsFamilyState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; -import { tableRowIdsByGroupComponentFamilyState } from '@/object-record/record-table/states/tableRowIdsByGroupComponentFamilyState'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -21,12 +21,12 @@ export const useSetRecordTableData = ({ recordTableId, onEntityCountChange, }: useSetRecordTableDataProps) => { - const tableRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - tableRowIdsByGroupComponentFamilyState, + const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( + recordIndexRowIdsByGroupComponentFamilyState, recordTableId, ); - const tableAllRowIdsState = useRecoilComponentCallbackStateV2( - tableAllRowIdsComponentState, + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, recordTableId, ); const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( @@ -37,8 +37,8 @@ export const useSetRecordTableData = ({ hasUserSelectedAllRowsComponentState, recordTableId, ); - const recordGroupDefinitionsState = useRecoilComponentCallbackStateV2( - recordGroupDefinitionsComponentState, + const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( + recordGroupIdsComponentState, recordTableId, ); @@ -46,11 +46,11 @@ export const useSetRecordTableData = ({ ({ set, snapshot }) => ({ records, - recordGroupId, + currentRecordGroupId, totalCount, }: { records: T[]; - recordGroupId?: string; + currentRecordGroupId?: string; totalCount?: number; }) => { for (const record of records) { @@ -66,9 +66,9 @@ export const useSetRecordTableData = ({ const currentRowIds = getSnapshotValue( snapshot, - recordGroupId - ? tableRowIdsByGroupFamilyState(recordGroupId) - : tableAllRowIdsState, + currentRecordGroupId + ? recordIndexRowIdsByGroupFamilyState(currentRecordGroupId) + : recordIndexAllRowIdsState, ); const hasUserSelectedAllRows = getSnapshotValue( @@ -76,9 +76,9 @@ export const useSetRecordTableData = ({ hasUserSelectedAllRowsState, ); - const recordGroupDefinitions = getSnapshotValue( + const recordGroupIds = getSnapshotValue( snapshot, - recordGroupDefinitionsState, + recordIndexRecordGroupIdsState, ); const recordIds = records.map((record) => record.id); @@ -90,39 +90,42 @@ export const useSetRecordTableData = ({ } } - if (isDefined(recordGroupId)) { + if (isDefined(currentRecordGroupId)) { // TODO: Hack to store all ids in the same order as the record group definitions // Should be replaced by something more efficient const allRowIds: string[] = []; - set(tableRowIdsByGroupFamilyState(recordGroupId), recordIds); + set( + recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + recordIds, + ); - for (const recordGroupDefinition of recordGroupDefinitions) { + for (const recordGroupId of recordGroupIds) { const tableRowIdsByGroup = - recordGroupDefinition.id !== recordGroupId + recordGroupId !== currentRecordGroupId ? getSnapshotValue( snapshot, - tableRowIdsByGroupFamilyState(recordGroupDefinition.id), + recordIndexRowIdsByGroupFamilyState(recordGroupId), ) : recordIds; allRowIds.push(...tableRowIdsByGroup); } - set(tableAllRowIdsState, allRowIds); + set(recordIndexAllRowIdsState, allRowIds); } else { - set(tableAllRowIdsState, recordIds); + set(recordIndexAllRowIdsState, recordIds); } onEntityCountChange(totalCount); } }, [ - tableRowIdsByGroupFamilyState, - tableAllRowIdsState, - recordGroupDefinitionsState, + recordIndexRowIdsByGroupFamilyState, + recordIndexAllRowIdsState, + hasUserSelectedAllRowsState, + recordIndexRecordGroupIdsState, onEntityCountChange, isRowSelectedFamilyState, - hasUserSelectedAllRowsState, ], ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts index 7694d5f75..bfbe8e005 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/hooks/useRecordTableMoveFocus.ts @@ -3,9 +3,9 @@ import { useRecoilCallback } from 'recoil'; import { MoveFocusDirection } from '@/object-record/record-table/types/MoveFocusDirection'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { useSetSoftFocusPosition } from './internal/useSetSoftFocusPosition'; @@ -17,8 +17,8 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { recordTableId, ); - const tableAllRowIdsState = useRecoilComponentCallbackStateV2( - tableAllRowIdsComponentState, + const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRowIdsComponentState, recordTableId, ); @@ -47,7 +47,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const moveDown = useRecoilCallback( ({ snapshot }) => () => { - const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); + const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); const softFocusPosition = getSnapshotValue( snapshot, softFocusPositionState, @@ -64,7 +64,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { row: newRowIndex, }); }, - [tableAllRowIdsState, setSoftFocusPosition, softFocusPositionState], + [recordIndexAllRowIdsState, setSoftFocusPosition, softFocusPositionState], ); const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( @@ -75,7 +75,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const moveRight = useRecoilCallback( ({ snapshot }) => () => { - const allRowIds = getSnapshotValue(snapshot, tableAllRowIdsState); + const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); const softFocusPosition = getSnapshotValue( snapshot, softFocusPositionState, @@ -116,7 +116,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { } }, [ - tableAllRowIdsState, + recordIndexAllRowIdsState, softFocusPositionState, numberOfTableColumnsSelector, setSoftFocusPosition, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext.tsx index 0bcc4ac78..cf3546e1d 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext.tsx @@ -3,10 +3,10 @@ import { ReactNode, useContext } from 'react'; import { useSetRecoilState } from 'recoil'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext'; import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { isDefined } from '~/utils/isDefined'; @@ -22,8 +22,8 @@ export const RecordTableBodyDragDropContext = ({ objectNameSingular, }); - const tableAllRowIds = useRecoilComponentValueV2( - tableAllRowIdsComponentState, + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, ); const { currentViewWithCombinedFiltersAndSorts } = @@ -43,7 +43,7 @@ export const RecordTableBodyDragDropContext = ({ return; } - const computeResult = computeNewRowPosition(result, tableAllRowIds); + const computeResult = computeNewRowPosition(result, allRowIds); if (!isDefined(computeResult)) { return; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx index f2a765d6d..19b13b6de 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody.tsx @@ -1,22 +1,22 @@ +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/components/RecordTableNoRecordGroupRows'; import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableNoRecordGroupBody = () => { - const tableAllRowIds = useRecoilComponentValueV2( - tableAllRowIdsComponentState, + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, ); const isRecordTableInitialLoading = useRecoilComponentValueV2( isRecordTableInitialLoadingComponentState, ); - if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { + if (isRecordTableInitialLoading && allRowIds.length === 0) { return ; } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect.tsx index 155a0dcc6..d768a6700 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect.tsx @@ -56,7 +56,7 @@ export const RecordTableRecordGroupBodyEffect = () => { if (!loading) { setRecordTableData({ records, - recordGroupId, + currentRecordGroupId: recordGroupId, totalCount, }); } diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects.tsx index 9efde27e3..ee45df94b 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects.tsx @@ -1,18 +1,15 @@ import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; -import { recordGroupDefinitionsComponentState } from '@/object-record/record-group/states/recordGroupDefinitionsComponentState'; +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; import { RecordTableRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffect'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableRecordGroupBodyEffects = () => { - const recordGroupDefinitions = useRecoilComponentValueV2( - recordGroupDefinitionsComponentState, + const recordGroupIds = useRecoilComponentValueV2( + recordGroupIdsComponentState, ); - return recordGroupDefinitions.map((recordGroupDefinition) => ( - + return recordGroupIds.map((recordGroupId) => ( + )); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx index d1f6ccb2f..5f1b2dc77 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody.tsx @@ -1,32 +1,28 @@ -import { useRecordGroups } from '@/object-record/record-group/hooks/useRecordGroups'; import { RecordGroupContext } from '@/object-record/record-group/states/context/RecordGroupContext'; +import { visibleRecordGroupIdsComponentSelector } from '@/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableRecordGroupRows } from '@/object-record/record-table/components/RecordTableRecordGroupRows'; import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable'; import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading'; import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -type RecordTableRecordGroupsBodyProps = { - objectNameSingular: string; -}; - -export const RecordTableRecordGroupsBody = ({ - objectNameSingular, -}: RecordTableRecordGroupsBodyProps) => { - const tableAllRowIds = useRecoilComponentValueV2( - tableAllRowIdsComponentState, +export const RecordTableRecordGroupsBody = () => { + const allRowIds = useRecoilComponentValueV2( + recordIndexAllRowIdsComponentState, ); const isRecordTableInitialLoading = useRecoilComponentValueV2( isRecordTableInitialLoadingComponentState, ); - const { visibleRecordGroups } = useRecordGroups({ objectNameSingular }); + const visibleRecordGroupIds = useRecoilComponentValueV2( + visibleRecordGroupIdsComponentSelector, + ); - if (isRecordTableInitialLoading && tableAllRowIds.length === 0) { + if (isRecordTableInitialLoading && allRowIds.length === 0) { return ; } @@ -34,10 +30,10 @@ export const RecordTableRecordGroupsBody = ({ - {visibleRecordGroups.map((recordGroupDefinition) => ( + {visibleRecordGroupIds.map((recordGroupId) => ( diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector.ts index 470b8d9dc..c9d1db2d3 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector.ts @@ -1,7 +1,7 @@ import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector'; +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus'; @@ -12,8 +12,9 @@ export const allRowsSelectedStatusComponentSelector = get: ({ instanceId }) => ({ get }) => { - const tableRowIds = get( - tableAllRowIdsComponentState.atomFamily({ + const allRowIds = get( + // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! + recordIndexAllRowIdsComponentState.atomFamily({ instanceId, }), ); @@ -29,7 +30,7 @@ export const allRowsSelectedStatusComponentSelector = const allRowsSelectedStatus = numberOfSelectedRows === 0 ? 'none' - : selectedRowIds.length === tableRowIds.length + : selectedRowIds.length === allRowIds.length ? 'all' : 'some'; diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/selectedRowIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/selectedRowIdsComponentSelector.ts index ced5cb600..734056d3f 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/selectedRowIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/selectedRowIdsComponentSelector.ts @@ -1,6 +1,6 @@ +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; export const selectedRowIdsComponentSelector = createComponentSelectorV2< @@ -11,13 +11,14 @@ export const selectedRowIdsComponentSelector = createComponentSelectorV2< get: ({ instanceId }) => ({ get }) => { - const rowIds = get( - tableAllRowIdsComponentState.atomFamily({ + const allRowIds = get( + // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! + recordIndexAllRowIdsComponentState.atomFamily({ instanceId, }), ); - return rowIds.filter( + return allRowIds.filter( (rowId) => get( isRowSelectedComponentFamilyState.atomFamily({ diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector.ts index 1579e41b0..00fcba743 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/states/selectors/unselectedRowIdsComponentSelector.ts @@ -1,6 +1,6 @@ +import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { tableAllRowIdsComponentState } from '@/object-record/record-table/states/tableAllRowIdsComponentState'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; export const unselectedRowIdsComponentSelector = createComponentSelectorV2< @@ -12,7 +12,8 @@ export const unselectedRowIdsComponentSelector = createComponentSelectorV2< ({ instanceId }) => ({ get }) => { const rowIds = get( - tableAllRowIdsComponentState.atomFamily({ + // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! + recordIndexAllRowIdsComponentState.atomFamily({ instanceId, }), ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/states/tableAllRowIdsComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/states/tableAllRowIdsComponentState.ts deleted file mode 100644 index e6f8ef4b2..000000000 --- a/packages/twenty-front/src/modules/object-record/record-table/states/tableAllRowIdsComponentState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; -import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; - -export const tableAllRowIdsComponentState = createComponentStateV2({ - key: 'tableAllRowIdsComponentState', - defaultValue: [], - componentInstanceContext: RecordTableComponentInstanceContext, -}); diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2.ts new file mode 100644 index 000000000..1286621e1 --- /dev/null +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2.ts @@ -0,0 +1,30 @@ +import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; +import { ComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/types/ComponentFamilyStateV2'; +import { globalComponentInstanceContextMap } from '@/ui/utilities/state/component-state/utils/globalComponentInstanceContextMap'; +import { SerializableParam, useRecoilState } from 'recoil'; + +export const useRecoilComponentFamilyStateV2 = < + StateType, + FamilyKey extends SerializableParam, +>( + componentState: ComponentFamilyStateV2, + familyKey: FamilyKey, + instanceIdFromProps?: string, +) => { + const componentInstanceContext = globalComponentInstanceContextMap.get( + componentState.key, + ); + + if (!componentInstanceContext) { + throw new Error( + `Instance context for key "${componentState.key}" is not defined`, + ); + } + + const instanceId = useAvailableComponentInstanceIdOrThrow( + componentInstanceContext, + instanceIdFromProps, + ); + + return useRecoilState(componentState.atomFamily({ instanceId, familyKey })); +}; diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts index 384b26284..246c03f03 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveCurrentViewGroups.ts @@ -20,6 +20,56 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => { viewBarComponentId, ); + const saveViewGroup = useRecoilCallback( + ({ snapshot }) => + async (viewGroupToSave: ViewGroup) => { + const currentViewId = snapshot + .getLoadable(currentViewIdCallbackState) + .getValue(); + + if (!currentViewId) { + return; + } + + const view = await getViewFromCache(currentViewId); + + if (isUndefinedOrNull(view)) { + return; + } + + const currentViewGroups = view.viewGroups; + + const existingField = currentViewGroups.find( + (currentViewGroup) => + currentViewGroup.fieldValue === viewGroupToSave.fieldValue, + ); + + if (isUndefinedOrNull(existingField)) { + return; + } + + if ( + isDeeplyEqual( + { + position: existingField.position, + isVisible: existingField.isVisible, + }, + { + position: viewGroupToSave.position, + isVisible: viewGroupToSave.isVisible, + }, + ) + ) { + return; + } + + await updateViewGroupRecords([ + { ...viewGroupToSave, id: existingField.id }, + ]); + }, + [currentViewIdCallbackState, getViewFromCache, updateViewGroupRecords], + ); + const saveViewGroups = useRecoilCallback( ({ snapshot }) => async (viewGroupsToSave: ViewGroup[]) => { @@ -91,6 +141,7 @@ export const useSaveCurrentViewGroups = (viewBarComponentId?: string) => { ); return { + saveViewGroup, saveViewGroups, }; }; diff --git a/packages/twenty-front/src/modules/views/utils/mapRecordGroupDefinitionsToViewGroups.ts b/packages/twenty-front/src/modules/views/utils/mapRecordGroupDefinitionsToViewGroups.ts index b92519451..0d1647b3e 100644 --- a/packages/twenty-front/src/modules/views/utils/mapRecordGroupDefinitionsToViewGroups.ts +++ b/packages/twenty-front/src/modules/views/utils/mapRecordGroupDefinitionsToViewGroups.ts @@ -1,17 +1,9 @@ import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; import { ViewGroup } from '@/views/types/ViewGroup'; +import { recordGroupDefinitionToViewGroup } from '@/views/utils/recordGroupDefinitionToViewGroup'; export const mapRecordGroupDefinitionsToViewGroups = ( groupDefinitions: RecordGroupDefinition[], ): ViewGroup[] => { - return groupDefinitions.map( - (groupDefinition): ViewGroup => ({ - __typename: 'ViewGroup', - id: groupDefinition.id, - fieldMetadataId: groupDefinition.fieldMetadataId, - position: groupDefinition.position, - isVisible: groupDefinition.isVisible ?? true, - fieldValue: groupDefinition.value ?? '', - }), - ); + return groupDefinitions.map(recordGroupDefinitionToViewGroup); }; diff --git a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts index 7c7aba97a..73f478247 100644 --- a/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts +++ b/packages/twenty-front/src/modules/views/utils/mapViewGroupsToRecordGroupDefinitions.ts @@ -1,3 +1,4 @@ +import { v4 } from 'uuid'; import { isDefined } from '~/utils/isDefined'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; @@ -42,16 +43,7 @@ export const mapViewGroupsToRecordGroupDefinitions = ({ ); if (!selectedOption) { - return { - id: 'no-value', - title: 'No Value', - type: RecordGroupDefinitionType.NoValue, - value: null, - position: viewGroup.position, - isVisible: viewGroup.isVisible, - fieldMetadataId: selectFieldMetadataItem.id, - color: 'transparent', - } satisfies RecordGroupDefinition; + return null; } return { @@ -65,8 +57,32 @@ export const mapViewGroupsToRecordGroupDefinitions = ({ isVisible: viewGroup.isVisible, } as RecordGroupDefinition; }) - .filter(isDefined) - .sort((a, b) => a.position - b.position); + .filter(isDefined); - return recordGroupDefinitionsFromViewGroups; + if (selectFieldMetadataItem.isNullable === true) { + const viewGroup = viewGroups.find( + (viewGroup) => viewGroup.fieldValue === '', + ); + + const noValueColumn = { + id: viewGroup?.id ?? v4(), + title: 'No Value', + type: RecordGroupDefinitionType.NoValue, + value: null, + position: + viewGroup?.position ?? + recordGroupDefinitionsFromViewGroups + .map((option) => option.position) + .reduce((a, b) => Math.max(a, b), 0) + 1, + isVisible: viewGroup?.isVisible ?? true, + fieldMetadataId: selectFieldMetadataItem.id, + color: 'transparent', + } satisfies RecordGroupDefinition; + + return [...recordGroupDefinitionsFromViewGroups, noValueColumn]; + } + + return recordGroupDefinitionsFromViewGroups.sort( + (a, b) => a.position - b.position, + ); }; diff --git a/packages/twenty-front/src/modules/views/utils/recordGroupDefinitionToViewGroup.ts b/packages/twenty-front/src/modules/views/utils/recordGroupDefinitionToViewGroup.ts new file mode 100644 index 000000000..74ae28f5f --- /dev/null +++ b/packages/twenty-front/src/modules/views/utils/recordGroupDefinitionToViewGroup.ts @@ -0,0 +1,15 @@ +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { ViewGroup } from '@/views/types/ViewGroup'; + +export const recordGroupDefinitionToViewGroup = ( + recordGroup: RecordGroupDefinition, +): ViewGroup => { + return { + __typename: 'ViewGroup', + id: recordGroup.id, + fieldMetadataId: recordGroup.fieldMetadataId, + position: recordGroup.position, + isVisible: recordGroup.isVisible ?? true, + fieldValue: recordGroup.value ?? '', + }; +};