From 812ed6ed6974b24b32e50bea9f847bd1f787cf55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20M?= Date: Thu, 28 Nov 2024 13:44:21 +0100 Subject: [PATCH] feat: record board component state refactor (#8779) Fix #8758 This PR is migrating the recoil component state from v1 to v2 for board. It also now share some states and logics between board and table, further can be done later. Lastly this PR fix an issue since the PR #8613 that was treating no-value as a normal record-group. --- ...tionsDropdownHiddenRecordGroupsContent.tsx | 30 ++-- .../ObjectOptionsDropdownMenuContent.tsx | 13 +- ...ptionsDropdownRecordGroupFieldsContent.tsx | 13 +- ...tOptionsDropdownRecordGroupSortContent.tsx | 21 ++- ...jectOptionsDropdownRecordGroupsContent.tsx | 50 +++---- .../hooks/useObjectOptionsForBoard.ts | 12 +- .../record-board/components/RecordBoard.tsx | 140 ++++++++++-------- .../components/RecordBoardHeader.tsx | 18 ++- .../hooks/internal/useRecordBoardStates.ts | 103 ------------- .../internal/useSetRecordBoardColumns.ts | 58 -------- .../internal/useSetRecordBoardRecordIds.ts | 63 -------- .../internal/useSetRecordIdsForColumn.ts | 55 ------- .../record-board/hooks/useRecordBoard.ts | 43 ------ .../hooks/useRecordBoardSelection.ts | 27 +++- .../hooks/useSetRecordBoardRecordIds.ts | 110 ++++++++++++++ .../hooks/useSetRecordIdsForColumn.ts | 109 ++++++++++++++ .../components/RecordBoardCard.tsx | 41 ++--- .../components/RecordBoardColumn.tsx | 27 ++-- .../RecordBoardColumnCardsContainer.tsx | 16 +- .../RecordBoardColumnDropdownMenu.tsx | 5 +- .../RecordBoardColumnFetchMoreLoader.tsx | 17 ++- .../RecordBoardColumnHeaderWrapper.tsx | 24 +-- .../hooks/useColumnNewCardActions.ts | 10 +- .../RecordBoardComponentInstanceContext.ts | 4 + ...rdBoardCardSelectedComponentFamilyState.ts | 6 +- ...ordBoardCompactModeActiveComponentState.ts | 6 +- ...BoardFetchingRecordsByColumnFamilyState.ts | 15 +- ...ecordBoardFetchingRecordsComponentState.ts | 7 - .../recordBoardColumnIdsComponentState.ts | 8 - .../recordBoardColumnsComponentFamilyState.ts | 8 - ...cordBoardFieldDefinitionsComponentState.ts | 6 +- .../recordBoardFiltersComponentState.ts | 7 - ...rdKanbanFieldMetadataNameComponentState.ts | 7 - ...RecordIdsByColumnIdComponentFamilyState.ts | 7 - ...ldFetchMoreInColumnComponentFamilyState.ts | 8 +- .../states/recordBoardSortsComponentState.ts | 7 - ...ecordBoardAllRecordIdsComponentSelector.ts | 26 ---- ...cordBoardColumnsComponentFamilySelector.ts | 41 ----- ...BoardSelectedRecordIdsComponentSelector.ts | 30 ++-- ...dShouldFetchMoreComponentFamilySelector.ts | 28 ---- ...isibleFieldDefinitionsComponentSelector.ts | 12 +- .../RecordGroupMenuItemDraggable.tsx | 22 ++- .../RecordGroupsVisibilityDropdownSection.tsx | 21 +-- .../hooks/useCurrentRecordGroupDefinition.ts | 35 +---- .../hooks/useRecordGroupActions.ts | 27 ++-- .../hooks/useRecordGroupReorder.ts | 92 ++++++++---- .../hooks/useRecordGroupVisibility.ts | 122 +++++++-------- .../record-group/hooks/useRecordGroups.ts | 58 -------- .../record-group/hooks/useSetRecordGroup.ts | 83 +++++++++++ .../recordGroupDefinitionFamilyState.ts | 10 ++ .../recordGroupFieldMetadataComponentState.ts | 11 ++ ...ate.ts => recordGroupIdsComponentState.ts} | 6 +- .../hasRecordGroupsComponentSelector.ts} | 12 +- .../hiddenRecordGroupIdsComponentSelector.ts | 34 +++++ ...recordGroupDefinitionsComponentSelector.ts | 37 +++++ .../visibleRecordGroupIdsComponentSelector.ts | 63 ++++++++ .../utils/sortRecordGroupDefinitions.ts | 8 +- .../record-group/utils/sortedInsert.ts | 20 +++ .../RecordIndexBoardColumnLoaderEffect.tsx | 18 +-- .../components/RecordIndexBoardDataLoader.tsx | 15 +- .../RecordIndexBoardDataLoaderEffect.tsx | 87 +++-------- .../components/RecordIndexContainer.tsx | 49 ++---- .../RecordIndexPageKanbanAddButton.tsx | 24 +-- .../RecordIndexPageKanbanAddMenuItem.tsx | 26 ++-- .../__tests__/useExportFetchRecords.test.ts | 50 ++++--- .../export/hooks/useExportFetchRecords.ts | 12 +- .../hooks/useLoadRecordIndexBoard.ts | 41 +++-- .../hooks/useLoadRecordIndexBoardColumn.ts | 16 +- .../hooks/useLoadRecordIndexTable.ts | 3 +- .../hooks/useRecordBoardRecordGqlFields.ts | 22 +-- .../useRecordIndexPageKanbanAddMenuItem.ts | 12 -- .../recordIndexAllRowIdsComponentState.ts | 10 ++ ...IndexRowIdsByGroupComponentFamilyState.ts} | 8 +- .../record-table/components/RecordTable.tsx | 16 +- .../RecordTableNoRecordGroupRows.tsx | 8 +- .../components/RecordTableRecordGroupRows.tsx | 10 +- .../components/RecordTableEmptyHandler.tsx | 8 +- .../internal/useResetTableRowSelection.ts | 12 +- .../hooks/internal/useSelectAllRows.ts | 14 +- .../hooks/internal/useSetRecordTableData.ts | 57 +++---- .../hooks/useRecordTableMoveFocus.ts | 14 +- .../RecordTableBodyDragDropContext.tsx | 8 +- .../RecordTableNoRecordGroupBody.tsx | 8 +- .../RecordTableRecordGroupBodyEffect.tsx | 2 +- .../RecordTableRecordGroupBodyEffects.tsx | 13 +- .../RecordTableRecordGroupsBody.tsx | 28 ++-- .../allRowsSelectedStatusComponentSelector.ts | 9 +- .../selectedRowIdsComponentSelector.ts | 9 +- .../unselectedRowIdsComponentSelector.ts | 5 +- .../states/tableAllRowIdsComponentState.ts | 8 - .../hooks/useRecoilComponentFamilyStateV2.ts | 30 ++++ .../views/hooks/useSaveCurrentViewGroups.ts | 51 +++++++ .../mapRecordGroupDefinitionsToViewGroups.ts | 12 +- .../mapViewGroupsToRecordGroupDefinitions.ts | 42 ++++-- .../utils/recordGroupDefinitionToViewGroup.ts | 15 ++ 95 files changed, 1355 insertions(+), 1316 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useRecordBoardStates.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardColumns.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordBoardRecordIds.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/internal/useSetRecordIdsForColumn.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/useRecordBoard.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordBoardRecordIds.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/hooks/useSetRecordIdsForColumn.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/isRecordBoardFetchingRecordsComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnIdsComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardColumnsComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardFiltersComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardKanbanFieldMetadataNameComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardRecordIdsByColumnIdComponentFamilyState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/recordBoardSortsComponentState.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardAllRecordIdsComponentSelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardColumnsComponentFamilySelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-board/states/selectors/recordBoardShouldFetchMoreComponentFamilySelector.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-group/hooks/useRecordGroups.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/hooks/useSetRecordGroup.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/states/recordGroupDefinitionFamilyState.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/states/recordGroupFieldMetadataComponentState.ts rename packages/twenty-front/src/modules/object-record/record-group/states/{recordGroupDefinitionsComponentState.ts => recordGroupIdsComponentState.ts} (72%) rename packages/twenty-front/src/modules/object-record/record-group/states/{hasRecordGroupDefinitionsComponentSelector.ts => selectors/hasRecordGroupsComponentSelector.ts} (52%) create mode 100644 packages/twenty-front/src/modules/object-record/record-group/states/selectors/hiddenRecordGroupIdsComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/states/selectors/recordGroupDefinitionsComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/states/selectors/visibleRecordGroupIdsComponentSelector.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-group/utils/sortedInsert.ts delete mode 100644 packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordIndexPageKanbanAddMenuItem.ts create mode 100644 packages/twenty-front/src/modules/object-record/record-index/states/recordIndexAllRowIdsComponentState.ts rename packages/twenty-front/src/modules/object-record/{record-table/states/tableRowIdsByGroupComponentFamilyState.ts => record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts} (50%) delete mode 100644 packages/twenty-front/src/modules/object-record/record-table/states/tableAllRowIdsComponentState.ts create mode 100644 packages/twenty-front/src/modules/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2.ts create mode 100644 packages/twenty-front/src/modules/views/utils/recordGroupDefinitionToViewGroup.ts 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 5e6810cfb0..35161f2fe3 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 bb86602423..2384420582 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 fcfeea029b..b2a8c623da 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 94db1268f1..19a892be16 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 ccb19275b7..3dba54dc38 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 4c5076d9f8..a28c71b8ee 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 b284532386..e62c658115 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 a26506254b..0000000000 --- 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 58e00b9900..0000000000 --- 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 e5b61e4e6d..0000000000 --- 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 25138622df..0000000000 --- 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 755703d0e9..0000000000 --- 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 baba7e9f9a..8c20d3933a 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 0000000000..0194a5bf36 --- /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 0000000000..3ba7c3ee78 --- /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 92d897a614..a456c89a3d 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 26072d4a7e..1d850a8507 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 d3e540902b..8cdc5c32ff 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 ed6045033f..bfbfb7e9d0 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 7a610c3dda..1143aaf5e9 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 1386b494d6..68de7adb56 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 cb087c0813..3926ceddbe 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 0000000000..1d83aab210 --- /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 8cb7c99f49..3b446676de 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 68741ee81a..bde53d616e 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 dfaaffc0ec..8ee4affb3c 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 c76a8777e3..0000000000 --- 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 3ae094376c..0000000000 --- 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 1530820d80..0000000000 --- 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 e8fb862be6..36696c0004 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 7d493b349e..0000000000 --- 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 26490c9298..0000000000 --- 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 9cb2f0df33..0000000000 --- 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 01dd190990..2dec1d6b87 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 d2aa0923f3..0000000000 --- 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 154da9313f..0000000000 --- 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 fefff84510..0000000000 --- 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 ce2b1e4cac..849d457359 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 225ffafac6..0000000000 --- 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 4b2732eb36..62ae69407b 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 70bd645af2..3a5d668ad0 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 e43afd1eef..e655a402d0 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 b99af28b69..b1bc9829d7 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 b0c738a0fb..9b1c038e1a 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 5f877bff34..aa26edec35 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 8f638dbbe4..0000000000 --- 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 0000000000..d84e393a08 --- /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 0000000000..80cf70f685 --- /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 0000000000..144b47a043 --- /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 56ec80fcc2..6665fb6e44 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 bccab902cc..7603240105 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 0000000000..4c57886cbf --- /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 0000000000..ce0e0432ea --- /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 0000000000..4c2332ebbd --- /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 45c3a68b60..afe2aa41a8 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 0000000000..5a7dd1027e --- /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 6f6a2e2bed..bde4108f5e 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 194580587d..e8bd37d053 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 02834d1e7f..81f64f3f6f 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 feda1b8b96..0da7a71721 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 65c8b13039..ace9040aee 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 effa5ce3a4..c209a6b0ee 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 e1bc40ca0e..94042f4e02 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 bcc82de307..5237622268 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 7b241909de..fee8cc97aa 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 99a5220335..2738118054 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 8162bb7dfc..d705ef14bc 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 aef238c2be..4386c29221 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 8e5604cb0f..0000000000 --- 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 0000000000..7e163fd08e --- /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 395f7f185b..fe35021c6e 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 ac2fd976d2..8347616380 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 f36fa08ae4..7febddcdfa 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 7b28090002..debedb73f5 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 82b51a2e16..409c30a461 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 24d54fb1ba..f715435c07 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 7a35ef87a6..dcfa9e6f54 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 7694d5f757..bfbe8e0050 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 0bcc4ac78c..cf3546e1d2 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 f2a765d6d4..19b13b6de2 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 155a0dcc6a..d768a67005 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 9efde27e35..ee45df94b8 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 d1f6ccb2fe..5f1b2dc771 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 470b8d9dcb..c9d1db2d32 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 ced5cb600f..734056d3fe 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 1579e41b06..00fcba743d 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 e6f8ef4b24..0000000000 --- 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 0000000000..1286621e10 --- /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 384b262841..246c03f03f 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 b925194519..0d1647b3e1 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 7c7aba97ab..73f4782479 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 0000000000..74ae28f5f5 --- /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 ?? '', + }; +};