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 238442058..d5fa0be68 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 @@ -3,15 +3,18 @@ import { IconChevronLeft, IconSettings, MenuItem, + MenuItemSelect, UndecoratedLink, useIcons, } from 'twenty-ui'; import { useObjectNamePluralFromSingular } from '@/object-metadata/hooks/useObjectNamePluralFromSingular'; +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; 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 { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; 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'; @@ -23,6 +26,7 @@ import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMe import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useLocation } from 'react-router-dom'; import { useSetRecoilState } from 'recoil'; +import { isDefined } from '~/utils/isDefined'; export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { const { getIcon } = useIcons(); @@ -43,16 +47,22 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { hiddenRecordGroupIdsComponentSelector, ); + const recordGroupFieldMetadataItem = useRecoilComponentValueV2( + recordGroupFieldMetadataComponentState, + ); + const { recordGroupFieldSearchInput, setRecordGroupFieldSearchInput, filteredRecordGroupFieldMetadataItems, } = useSearchRecordGroupField(); - const { handleRecordGroupFieldChange, resetRecordGroupField } = - useHandleRecordGroupField({ - viewBarComponentId: recordIndexId, - }); + const { + handleRecordGroupFieldChange: setRecordGroupField, + resetRecordGroupField, + } = useHandleRecordGroupField({ + viewBarComponentId: recordIndexId, + }); const newFieldSettingsUrl = getSettingsPagePath( SettingsPath.ObjectNewFieldSelect, @@ -66,6 +76,18 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { navigationMemorizedUrlState, ); + const handleResetRecordGroupField = () => { + resetRecordGroupField(); + closeDropdown(); + }; + + const handleRecordGroupFieldChange = ( + fieldMetadataItem: FieldMetadataItem, + ) => { + setRecordGroupField(fieldMetadataItem); + closeDropdown(); + }; + useEffect(() => { if ( currentContentId === 'hiddenRecordGroups' && @@ -90,13 +112,16 @@ export const ObjectOptionsDropdownRecordGroupFieldsContent = () => { onChange={(event) => setRecordGroupFieldSearchInput(event.target.value)} /> - + {filteredRecordGroupFieldMetadataItems.map((fieldMetadataItem) => ( - { - handleRecordGroupFieldChange(fieldMetadataItem); - }} + selected={fieldMetadataItem.id === recordGroupFieldMetadataItem?.id} + onClick={() => handleRecordGroupFieldChange(fieldMetadataItem)} LeftIcon={getIcon(fieldMetadataItem.icon)} text={fieldMetadataItem.label} /> 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 a28c71b8e..897a9fd91 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 @@ -16,8 +16,8 @@ import { RecordBoardComponentInstanceContext } from '@/object-record/record-boar 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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -69,12 +69,13 @@ export const RecordBoard = () => { visibleRecordGroupIdsComponentSelector, ); - const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - recordIndexRowIdsByGroupComponentFamilyState, - ); + const recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, + const recordIndexAllRecordIdsState = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, ); const { resetRecordSelection, setRecordAsSelected } = @@ -97,14 +98,14 @@ export const RecordBoard = () => { () => { const allRecordIds = getSnapshotValue( snapshot, - recordIndexAllRowIdsState, + recordIndexAllRecordIdsState, ); for (const recordId of allRecordIds) { setRecordAsSelected(recordId, true); } }, - [recordIndexAllRowIdsState, setRecordAsSelected], + [recordIndexAllRecordIdsState, setRecordAsSelected], ); useScopedHotkeys('ctrl+a,meta+a', selectAll, TableHotkeyScope.Table); @@ -137,7 +138,7 @@ export const RecordBoard = () => { const destinationRecordByGroupIds = getSnapshotValue( snapshot, - recordIndexRowIdsByGroupFamilyState(destinationRecordGroupId), + recordIndexRecordIdsByGroupFamilyState(destinationRecordGroupId), ); const otherRecordIdsInDestinationColumn = sourceRecordGroupId === destinationRecordGroupId @@ -172,7 +173,7 @@ export const RecordBoard = () => { }); }, [ - recordIndexRowIdsByGroupFamilyState, + recordIndexRecordIdsByGroupFamilyState, selectFieldMetadataItem, updateOneRecord, ], 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 index 0194a5bf3..60916cfea 100644 --- 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 @@ -3,8 +3,7 @@ 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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -22,24 +21,15 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { recordBoardId, ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, - recordBoardId, - ); - - const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - recordIndexRowIdsByGroupComponentFamilyState, - recordBoardId, - ); + const recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + recordBoardId, + ); const setRecordIds = useRecoilCallback( ({ set, snapshot }) => (records: ObjectRecord[]) => { - const existingAllRowIds = getSnapshotValue( - snapshot, - recordIndexAllRowIdsState, - ); - const recordGroupIds = getSnapshotValue( snapshot, visibleRecordGroupIdsSelector, @@ -53,7 +43,7 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { const existingRecordGroupRowIds = getSnapshotValue( snapshot, - recordIndexRowIdsByGroupFamilyState(recordGroupId), + recordIndexRecordIdsByGroupFamilyState(recordGroupId), ); const recordGroupFieldMetadata = getSnapshotValue( @@ -75,32 +65,16 @@ export const useSetRecordBoardRecordIds = (recordBoardId?: string) => { if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) { set( - recordIndexRowIdsByGroupFamilyState(recordGroupId), + recordIndexRecordIdsByGroupFamilyState(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, + recordIndexRecordIdsByGroupFamilyState, recordGroupFieldMetadataState, - recordIndexAllRowIdsState, ], ); 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 index 3ba7c3ee7..35a0dda04 100644 --- 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 @@ -2,9 +2,7 @@ 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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { sortRecordsByPosition } from '@/object-record/utils/sortRecordsByPosition'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -13,36 +11,20 @@ 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 recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + 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), @@ -50,7 +32,7 @@ export const useSetRecordIdsForColumn = (recordBoardId?: string) => { const existingRecordGroupRowIds = getSnapshotValue( snapshot, - recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + recordIndexRecordIdsByGroupFamilyState(currentRecordGroupId), ); const recordGroupFieldMetadata = getSnapshotValue( @@ -72,35 +54,12 @@ export const useSetRecordIdsForColumn = (recordBoardId?: string) => { if (!isDeeplyEqual(existingRecordGroupRowIds, recordGroupRowIds)) { set( - recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + recordIndexRecordIdsByGroupFamilyState(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, - ], + [recordIndexRecordIdsByGroupFamilyState, recordGroupFieldMetadataState], ); return { 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 1d850a850..c76d85c2f 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 @@ -4,7 +4,7 @@ import { Droppable } from '@hello-pangea/dnd'; 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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilValue } from 'recoil'; @@ -31,8 +31,8 @@ export const RecordBoardColumn = ({ recordGroupDefinitionFamilyState(recordBoardColumnId), ); - const recordRowIdsByGroup = useRecoilComponentFamilyValueV2( - recordIndexRowIdsByGroupComponentFamilyState, + const recordIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRecordIdsByGroupComponentFamilyState, recordBoardColumnId, ); @@ -44,9 +44,9 @@ export const RecordBoardColumn = ({ @@ -54,7 +54,7 @@ export const RecordBoardColumn = ({ )} 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 68de7adb5..c6873baa3 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 @@ -3,7 +3,7 @@ import { isDefined } from 'twenty-ui'; 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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useRecoilValue } from 'recoil'; @@ -18,8 +18,8 @@ export const RecordBoardColumnHeaderWrapper = ({ recordGroupDefinitionFamilyState(columnId), ); - const recordRowIdsByGroup = useRecoilComponentFamilyValueV2( - recordIndexRowIdsByGroupComponentFamilyState, + const recordIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRecordIdsByGroupComponentFamilyState, columnId, ); @@ -32,8 +32,8 @@ export const RecordBoardColumnHeaderWrapper = ({ value={{ columnId, columnDefinition: recordGroupDefinition, - recordCount: recordRowIdsByGroup.length, - recordIds: recordRowIdsByGroup, + recordCount: recordIdsByGroup.length, + recordIds: recordIdsByGroup, }} > 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 849d45735..1a4be45f4 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,6 +1,6 @@ import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext'; import { isRecordBoardCardSelectedComponentFamilyState } from '@/object-record/record-board/states/isRecordBoardCardSelectedComponentFamilyState'; -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; export const recordBoardSelectedRecordIdsComponentSelector = @@ -10,11 +10,15 @@ export const recordBoardSelectedRecordIdsComponentSelector = get: ({ instanceId }) => ({ get }) => { - const allRowIds = get( - recordIndexAllRowIdsComponentState.atomFamily({ instanceId }), + const allRecordIds = get( + // TODO: This selector use a context different from the one used in the snippet + // its working for now as the instanceId is the same but we should change this + recordIndexAllRecordIdsComponentSelector.selectorFamily({ + instanceId, + }), ); - return allRowIds.filter( + return allRecordIds.filter( (recordId) => get( isRecordBoardCardSelectedComponentFamilyState.atomFamily({ 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 aa26edec3..734dc61c8 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 @@ -2,7 +2,7 @@ import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/s 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 { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useSaveCurrentViewGroups } from '@/views/hooks/useSaveCurrentViewGroups'; @@ -22,10 +22,11 @@ export const useRecordGroupVisibility = ({ recordGroupIdsComponentState, ); - const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - recordIndexRowIdsByGroupComponentFamilyState, - viewBarId, - ); + const recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + viewBarId, + ); const objectOptionsDropdownRecordGroupHideState = useRecoilComponentCallbackStateV2(recordIndexRecordGroupHideComponentState); @@ -79,7 +80,7 @@ export const useRecordGroupVisibility = ({ const recordGroupRowIds = getSnapshotValue( snapshot, - recordIndexRowIdsByGroupFamilyState(recordGroupId), + recordIndexRecordIdsByGroupFamilyState(recordGroupId), ); if (recordGroupRowIds.length > 0) { @@ -107,7 +108,7 @@ export const useRecordGroupVisibility = ({ recordIndexRecordGroupIdsState, objectOptionsDropdownRecordGroupHideState, saveViewGroups, - recordIndexRowIdsByGroupFamilyState, + recordIndexRecordIdsByGroupFamilyState, ], ); 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 index d84e393a0..934208dc5 100644 --- 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 @@ -8,7 +8,6 @@ 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); @@ -26,28 +25,23 @@ export const useSetRecordGroup = (viewId?: string) => { 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 fieldMetadataId = recordGroups?.[0]?.fieldMetadataId; + const fieldMetadata = fieldMetadataId + ? objectMetadataItem.fields.find( + (field) => field.id === fieldMetadataId, + ) + : undefined; const currentFieldMetadata = getSnapshotValue( snapshot, recordGroupFieldMetadataState, ); // Set the field metadata linked to the record groups - if ( - isDefined(fieldMetadata) && - !isDeeplyEqual(fieldMetadata, currentFieldMetadata) - ) { + if (!isDeeplyEqual(fieldMetadata, currentFieldMetadata)) { set(recordGroupFieldMetadataState, fieldMetadata); } @@ -67,6 +61,16 @@ export const useSetRecordGroup = (viewId?: string) => { const recordGroupIds = recordGroups.map(({ id }) => id); + // Get ids that has been removed between the current and new record groups + const removedRecordGroupIds = currentRecordGroupId.filter( + (id) => !recordGroupIds.includes(id), + ); + + // Remove the record groups that has been removed + removedRecordGroupIds.forEach((id) => { + set(recordGroupDefinitionFamilyState(id), undefined); + }); + if (isDeeplyEqual(currentRecordGroupId, recordGroupIds)) { return; } 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 index 4c57886cb..897447249 100644 --- 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 @@ -1,12 +1,13 @@ 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 hiddenRecordGroupIdsComponentSelector = createComponentSelectorV2< - string[] + RecordGroupDefinition['id'][] >({ key: 'hiddenRecordGroupIdsComponentSelector', componentInstanceContext: ViewComponentInstanceContext, 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 index 4c2332ebb..848cebbad 100644 --- 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 @@ -10,7 +10,7 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon import { isDefined } from '~/utils/isDefined'; export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2< - string[] + RecordGroupDefinition['id'][] >({ key: 'visibleRecordGroupIdsComponentSelector', componentInstanceContext: ViewComponentInstanceContext, 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 afe2aa41a..09fa73eb8 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 @@ -6,7 +6,7 @@ export const sortRecordGroupDefinitions = ( recordGroupSort: RecordGroupSort, ) => { const visibleRecordGroups = recordGroupDefinitions.filter( - (boardGroup) => boardGroup.isVisible, + (recordGroup) => recordGroup.isVisible, ); const compareAlphabetical = (a: string, b: string, reverse = false) => { 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 d705ef14b..ccfec9af3 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 @@ -61,6 +61,10 @@ export const useFindManyParams = ( ); } + if (!isDefined(currentRecordGroupDefinition.value)) { + return { [fieldMetadataItem.name]: { is: 'NULL' } }; + } + return { [fieldMetadataItem.name]: { eq: currentRecordGroupDefinition.value, @@ -68,8 +72,6 @@ export const useFindManyParams = ( }; } - // TODO: Handle case when value is nullable - return {}; }, [objectMetadataItem.fields, currentRecordGroupDefinition]); 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 deleted file mode 100644 index 7e163fd08..000000000 --- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexAllRowIdsComponentState.ts +++ /dev/null @@ -1,10 +0,0 @@ -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-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState.ts similarity index 79% rename from packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts rename to packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState.ts index fe35021c6..e8b1aa1cd 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState.ts @@ -2,9 +2,9 @@ import { RecordGroupDefinition } from '@/object-record/record-group/types/Record import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; -export const recordIndexRowIdsByGroupComponentFamilyState = +export const recordIndexRecordIdsByGroupComponentFamilyState = createComponentFamilyStateV2({ - key: 'recordIndexRowIdsByGroupComponentFamilyState', + key: 'recordIndexRecordIdsByGroupComponentFamilyState', defaultValue: [], componentInstanceContext: ViewComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector.ts new file mode 100644 index 000000000..eb4239357 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector.ts @@ -0,0 +1,59 @@ +import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { ObjectRecord } from '@/object-record/types/ObjectRecord'; +import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +/** + * Do not use this key outside of this file. + * This is a temporary key to store the record ids for the default record group. + */ +const defaultFamilyKey = 'record-group-default-id'; + +export const recordIndexAllRecordIdsComponentSelector = + createComponentSelectorV2({ + key: 'recordIndexAllRecordIdsComponentSelector', + componentInstanceContext: ViewComponentInstanceContext, + get: + ({ instanceId }) => + ({ get }) => { + const recordGroupIds = get( + recordGroupIdsComponentState.atomFamily({ + instanceId, + }), + ); + + if (recordGroupIds.length === 0) { + return get( + recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: defaultFamilyKey, + }), + ); + } + + return recordGroupIds.reduce( + (acc, recordGroupId) => { + const rowIds = get( + recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: recordGroupId, + }), + ); + + return [...acc, ...rowIds]; + }, + [], + ); + }, + set: + ({ instanceId }) => + ({ set }, recordIds) => + set( + recordIndexRecordIdsByGroupComponentFamilyState.atomFamily({ + instanceId, + familyKey: defaultFamilyKey, + }), + recordIds, + ), + }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector.ts b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector.ts index 077216691..d1e1606eb 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/states/selectors/recordIndexRecordGroupIsDraggableSortComponentSelector.ts @@ -4,7 +4,7 @@ import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; export const recordIndexRecordGroupIsDraggableSortComponentSelector = - createComponentSelectorV2({ + createComponentSelectorV2({ key: 'recordIndexRecordGroupIsDraggableSortComponentSelector', componentInstanceContext: ViewComponentInstanceContext, get: 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 834761638..4d6f5331b 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 @@ -2,7 +2,7 @@ import styled from '@emotion/styled'; import { isNonEmptyString, isNull } from '@sniptt/guards'; import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector'; -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -53,8 +53,8 @@ export const RecordTable = ({ recordTableId, ); - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, recordTableId, ); @@ -70,7 +70,7 @@ export const RecordTable = ({ const recordTableIsEmpty = !isRecordTableInitialLoading && - allRowIds.length === 0 && + allRecordIds.length === 0 && isNull(pendingRecordId); const { resetTableRowSelection, setRowSelected } = useRecordTable({ 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 7febddcdf..35ef9bf2b 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,16 +1,16 @@ -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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 { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableNoRecordGroupRows = () => { - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, ); return ( <> - {allRowIds.map((recordId, rowIndex) => { + {allRecordIds.map((recordId, rowIndex) => { return ( { - const recordGroupId = useCurrentRecordGroupId(); + const currentRecordGroupId = useCurrentRecordGroupId(); - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, ); - const recordGroupRowIds = useRecoilComponentFamilyValueV2( - recordIndexRowIdsByGroupComponentFamilyState, - recordGroupId, + const recordIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRecordIdsByGroupComponentFamilyState, + currentRecordGroupId, + ); + + const isRecordGroupTableSectionToggled = useRecoilComponentFamilyValueV2( + isRecordGroupTableSectionToggledComponentState, + currentRecordGroupId, ); const rowIndexMap = useMemo( - () => new Map(allRowIds.map((id, index) => [id, index])), - [allRowIds], + () => new Map(allRecordIds.map((recordId, index) => [recordId, index])), + [allRecordIds], ); - return recordGroupRowIds.map((recordId) => { - const rowIndex = rowIndexMap.get(recordId); + return ( + isRecordGroupTableSectionToggled && + recordIdsByGroup.map((recordId) => { + const rowIndex = rowIndexMap.get(recordId); - if (!rowIndex) { - throw new Error(`Row index for record id ${recordId} not found`); - } + if (!isDefined(rowIndex)) { + return null; + } - return ( - - ); - }); + return ( + + ); + }) + ); }; 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 debedb73f..d0bb45047 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,6 +1,6 @@ import { isNull } from '@sniptt/guards'; -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -20,8 +20,8 @@ export const RecordTableEmptyHandler = ({ recordTableId, ); - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, recordTableId, ); @@ -32,7 +32,7 @@ export const RecordTableEmptyHandler = ({ const recordTableIsEmpty = !isRecordTableInitialLoading && - allRowIds.length === 0 && + allRecordIds.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 409c30a46..e3c1095ee 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,7 +2,7 @@ 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 { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -18,8 +18,8 @@ export const useResetTableRowSelection = (recordTableId?: string) => { recordTableId, ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, + const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, recordTableIdFromContext, ); @@ -43,10 +43,13 @@ export const useResetTableRowSelection = (recordTableId?: string) => { return useRecoilCallback( ({ set, snapshot }) => () => { - const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); + const allRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRecordIdsSelector, + ); - for (const rowId of allRowIds) { - set(isRowSelectedFamilyState(rowId), false); + for (const recordId of allRecordIds) { + set(isRowSelectedFamilyState(recordId), false); } set(hasUserSelectedAllRowsState, false); @@ -54,7 +57,7 @@ export const useResetTableRowSelection = (recordTableId?: string) => { set(isActionMenuDropdownOpenState, false); }, [ - recordIndexAllRowIdsState, + recordIndexAllRecordIdsSelector, 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 f715435c0..45d20105b 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,6 +1,6 @@ import { useRecoilCallback } from 'recoil'; -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; @@ -15,8 +15,8 @@ export const useSelectAllRows = (recordTableId?: string) => { isRowSelectedComponentFamilyState, recordTableId, ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, + const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, recordTableId, ); @@ -28,24 +28,22 @@ export const useSelectAllRows = (recordTableId?: string) => { allRowsSelectedStatusSelector, ); - const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); + const allRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRecordIdsSelector, + ); - if ( - allRowsSelectedStatus === 'none' || - allRowsSelectedStatus === 'some' - ) { - for (const rowId of allRowIds) { - set(isRowSelectedFamilyState(rowId), true); - } - } else { - for (const rowId of allRowIds) { - set(isRowSelectedFamilyState(rowId), false); - } + for (const recordId of allRecordIds) { + const isSelected = + allRowsSelectedStatus === 'none' || + allRowsSelectedStatus === 'some'; + + set(isRowSelectedFamilyState(recordId), isSelected); } }, [ allRowsSelectedStatusSelector, - recordIndexAllRowIdsState, + recordIndexAllRecordIdsSelector, 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 dcfa9e6f5..dc09fd20a 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,8 +1,7 @@ import { useRecoilCallback } from 'recoil'; -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 { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -21,26 +20,26 @@ export const useSetRecordTableData = ({ recordTableId, onEntityCountChange, }: useSetRecordTableDataProps) => { - const recordIndexRowIdsByGroupFamilyState = useRecoilComponentCallbackStateV2( - recordIndexRowIdsByGroupComponentFamilyState, - recordTableId, - ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, + const recordIndexRecordIdsByGroupFamilyState = + useRecoilComponentCallbackStateV2( + recordIndexRecordIdsByGroupComponentFamilyState, + recordTableId, + ); + + const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, recordTableId, ); + const isRowSelectedFamilyState = useRecoilComponentCallbackStateV2( isRowSelectedComponentFamilyState, recordTableId, ); + const hasUserSelectedAllRowsState = useRecoilComponentCallbackStateV2( hasUserSelectedAllRowsComponentState, recordTableId, ); - const recordIndexRecordGroupIdsState = useRecoilComponentCallbackStateV2( - recordGroupIdsComponentState, - recordTableId, - ); return useRecoilCallback( ({ set, snapshot }) => @@ -67,8 +66,8 @@ export const useSetRecordTableData = ({ const currentRowIds = getSnapshotValue( snapshot, currentRecordGroupId - ? recordIndexRowIdsByGroupFamilyState(currentRecordGroupId) - : recordIndexAllRowIdsState, + ? recordIndexRecordIdsByGroupFamilyState(currentRecordGroupId) + : recordIndexAllRecordIdsSelector, ); const hasUserSelectedAllRows = getSnapshotValue( @@ -76,11 +75,6 @@ export const useSetRecordTableData = ({ hasUserSelectedAllRowsState, ); - const recordGroupIds = getSnapshotValue( - snapshot, - recordIndexRecordGroupIdsState, - ); - const recordIds = records.map((record) => record.id); if (!isDeeplyEqual(currentRowIds, recordIds)) { @@ -91,39 +85,21 @@ export const useSetRecordTableData = ({ } 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( - recordIndexRowIdsByGroupFamilyState(currentRecordGroupId), + recordIndexRecordIdsByGroupFamilyState(currentRecordGroupId), recordIds, ); - - for (const recordGroupId of recordGroupIds) { - const tableRowIdsByGroup = - recordGroupId !== currentRecordGroupId - ? getSnapshotValue( - snapshot, - recordIndexRowIdsByGroupFamilyState(recordGroupId), - ) - : recordIds; - - allRowIds.push(...tableRowIdsByGroup); - } - set(recordIndexAllRowIdsState, allRowIds); } else { - set(recordIndexAllRowIdsState, recordIds); + set(recordIndexAllRecordIdsSelector, recordIds); } onEntityCountChange(totalCount); } }, [ - recordIndexRowIdsByGroupFamilyState, - recordIndexAllRowIdsState, + recordIndexRecordIdsByGroupFamilyState, + recordIndexAllRecordIdsSelector, hasUserSelectedAllRowsState, - recordIndexRecordGroupIdsState, onEntityCountChange, isRowSelectedFamilyState, ], 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 bfbe8e005..8e97bebe2 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,7 +3,7 @@ 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 { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { numberOfTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/numberOfTableColumnsComponentSelector'; import { softFocusPositionComponentState } from '@/object-record/record-table/states/softFocusPositionComponentState'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; @@ -17,8 +17,8 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { recordTableId, ); - const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2( - recordIndexAllRowIdsComponentState, + const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, recordTableId, ); @@ -47,7 +47,10 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const moveDown = useRecoilCallback( ({ snapshot }) => () => { - const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); + const allRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRecordIdsSelector, + ); const softFocusPosition = getSnapshotValue( snapshot, softFocusPositionState, @@ -55,8 +58,8 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { let newRowIndex = softFocusPosition.row + 1; - if (newRowIndex >= allRowIds.length) { - newRowIndex = allRowIds.length - 1; + if (newRowIndex >= allRecordIds.length) { + newRowIndex = allRecordIds.length - 1; } setSoftFocusPosition({ @@ -64,7 +67,11 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { row: newRowIndex, }); }, - [recordIndexAllRowIdsState, setSoftFocusPosition, softFocusPositionState], + [ + recordIndexAllRecordIdsSelector, + setSoftFocusPosition, + softFocusPositionState, + ], ); const numberOfTableColumnsSelector = useRecoilComponentCallbackStateV2( @@ -75,7 +82,10 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const moveRight = useRecoilCallback( ({ snapshot }) => () => { - const allRowIds = getSnapshotValue(snapshot, recordIndexAllRowIdsState); + const allRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRecordIdsSelector, + ); const softFocusPosition = getSnapshotValue( snapshot, softFocusPositionState, @@ -91,11 +101,11 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { const isLastRowAndLastColumn = currentColumnIndex === numberOfTableColumns - 1 && - currentRowIndex === allRowIds.length - 1; + currentRowIndex === allRecordIds.length - 1; const isLastColumnButNotLastRow = currentColumnIndex === numberOfTableColumns - 1 && - currentRowIndex !== allRowIds.length - 1; + currentRowIndex !== allRecordIds.length - 1; const isNotLastColumn = currentColumnIndex !== numberOfTableColumns - 1; @@ -116,7 +126,7 @@ export const useRecordTableMoveFocus = (recordTableId?: string) => { } }, [ - recordIndexAllRowIdsState, + recordIndexAllRecordIdsSelector, softFocusPositionState, numberOfTableColumnsSelector, setSoftFocusPosition, diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx new file mode 100644 index 000000000..7a6dbe297 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBody.tsx @@ -0,0 +1,50 @@ +import styled from '@emotion/styled'; +import { MOBILE_VIEWPORT } from 'twenty-ui'; + +const StyledTbody = styled.tbody` + &.first-columns-sticky { + td:nth-of-type(1) { + position: sticky; + left: 0; + z-index: 5; + transition: 0.3s ease; + } + td:nth-of-type(2) { + position: sticky; + left: 11px; + z-index: 5; + transition: 0.3s ease; + } + td:nth-of-type(3) { + position: sticky; + left: 43px; + z-index: 5; + transition: 0.3s ease; + + @media (max-width: ${MOBILE_VIEWPORT}px) { + & [data-testid='editable-cell-display-mode'] { + [data-testid='tooltip'] { + display: none; + } + + [data-testid='chip'] { + gap: 0; + } + } + } + + &::after { + content: ''; + position: absolute; + top: -1px; + height: calc(100% + 2px); + width: 4px; + right: 0px; + box-shadow: ${({ theme }) => theme.boxShadow.light}; + clip-path: inset(0px -4px 0px 0px); + } + } + } +`; + +export const RecordTableBody = StyledTbody; 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/RecordTableBodyDragDropContextProvider.tsx similarity index 83% rename from packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext.tsx rename to packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContextProvider.tsx index cf3546e1d..4c5575fd0 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/RecordTableBodyDragDropContextProvider.tsx @@ -3,7 +3,7 @@ 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 { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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'; @@ -11,7 +11,7 @@ import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/ import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { isDefined } from '~/utils/isDefined'; -export const RecordTableBodyDragDropContext = ({ +export const RecordTableBodyDragDropContextProvider = ({ children, }: { children: ReactNode; @@ -22,8 +22,8 @@ export const RecordTableBodyDragDropContext = ({ objectNameSingular, }); - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, ); const { currentViewWithCombinedFiltersAndSorts } = @@ -43,7 +43,7 @@ export const RecordTableBodyDragDropContext = ({ return; } - const computeResult = computeNewRowPosition(result, allRowIds); + const computeResult = computeNewRowPosition(result, allRecordIds); if (!isDefined(computeResult)) { return; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx index b78b74663..c8a41ef95 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyDroppable.tsx @@ -1,80 +1,36 @@ -import { Theme } from '@emotion/react'; +import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody'; import { Droppable } from '@hello-pangea/dnd'; -import { styled } from '@linaria/react'; -import { ReactNode, useContext, useState } from 'react'; -import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui'; +import { ReactNode, useState } from 'react'; import { v4 } from 'uuid'; -const StyledTbody = styled.tbody<{ - theme: Theme; -}>` - &.first-columns-sticky { - td:nth-of-type(1) { - position: sticky; - left: 0; - z-index: 5; - transition: 0.3s ease; - } - td:nth-of-type(2) { - position: sticky; - left: 11px; - z-index: 5; - transition: 0.3s ease; - } - td:nth-of-type(3) { - position: sticky; - left: 43px; - z-index: 5; - transition: 0.3s ease; - - @media (max-width: ${MOBILE_VIEWPORT}px) { - & [data-testid='editable-cell-display-mode'] { - [data-testid='tooltip'] { - display: none; - } - - [data-testid='chip'] { - gap: 0; - } - } - } - - &::after { - content: ''; - position: absolute; - top: -1px; - height: calc(100% + 2px); - width: 4px; - right: 0px; - box-shadow: ${({ theme }) => theme.boxShadow.light}; - clip-path: inset(0px -4px 0px 0px); - } - } - } -`; +type RecordTableBodyDroppableProps = { + children: ReactNode; + recordGroupId?: string; + isDropDisabled?: boolean; +}; export const RecordTableBodyDroppable = ({ children, -}: { - children: ReactNode; -}) => { + recordGroupId, + isDropDisabled, +}: RecordTableBodyDroppableProps) => { const [v4Persistable] = useState(v4()); - const { theme } = useContext(ThemeContext); - return ( - + {(provided) => ( - {children} {provided.placeholder} - + )} ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx new file mode 100644 index 000000000..5aaa38077 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider.tsx @@ -0,0 +1,106 @@ +import { DragDropContext, DropResult } from '@hello-pangea/dnd'; +import { ReactNode, useContext } from 'react'; +import { useRecoilCallback, useSetRecoilState } from 'recoil'; + +import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; +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 { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; +import { isDefined } from '~/utils/isDefined'; + +export const RecordTableBodyRecordGroupDragDropContextProvider = ({ + children, +}: { + children: ReactNode; +}) => { + const { objectNameSingular, recordTableId, objectMetadataItem } = + useContext(RecordTableContext); + + const { updateOneRecord: updateOneRow } = useUpdateOneRecord({ + objectNameSingular, + }); + + const recordIndexAllRecordIdsSelector = useRecoilComponentCallbackStateV2( + recordIndexAllRecordIdsComponentSelector, + ); + + const { currentViewWithCombinedFiltersAndSorts } = + useGetCurrentView(recordTableId); + + const viewSorts = currentViewWithCombinedFiltersAndSorts?.viewSorts || []; + + const setIsRemoveSortingModalOpenState = useSetRecoilState( + isRemoveSortingModalOpenState, + ); + + const computeNewRowPosition = useComputeNewRowPosition(); + + const handleDragEnd = useRecoilCallback( + ({ snapshot }) => + (result: DropResult) => { + const tableAllRecordIds = getSnapshotValue( + snapshot, + recordIndexAllRecordIdsSelector, + ); + + const recordGroupId = result.destination?.droppableId; + + if (!isDefined(recordGroupId)) { + throw new Error('Record group id is not defined'); + } + + const recordGroup = getSnapshotValue( + snapshot, + recordGroupDefinitionFamilyState(recordGroupId), + ); + + if (!isDefined(recordGroup)) { + throw new Error('Record group is not defined'); + } + + const fieldMetadata = objectMetadataItem.fields.find( + (field) => field.id === recordGroup.fieldMetadataId, + ); + + if (!isDefined(fieldMetadata)) { + throw new Error('Field metadata is not defined'); + } + + if (viewSorts.length > 0) { + setIsRemoveSortingModalOpenState(true); + return; + } + + const computeResult = computeNewRowPosition(result, tableAllRecordIds); + + if (!isDefined(computeResult)) { + return; + } + + updateOneRow({ + idToUpdate: computeResult.draggedRecordId, + updateOneRecordInput: { + position: computeResult.newPosition, + [fieldMetadata.name]: recordGroup.value, + }, + }); + }, + [ + recordIndexAllRecordIdsSelector, + objectMetadataItem.fields, + viewSorts.length, + computeNewRowPosition, + updateOneRow, + setIsRemoveSortingModalOpenState, + ], + ); + + return ( + {children} + ); +}; 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 19b13b6de..47e7e5f8f 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,6 +1,6 @@ -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { RecordTableNoRecordGroupRows } from '@/object-record/record-table/components/RecordTableNoRecordGroupRows'; -import { RecordTableBodyDragDropContext } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContext'; +import { RecordTableBodyDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDragDropContextProvider'; 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'; @@ -8,24 +8,24 @@ import { isRecordTableInitialLoadingComponentState } from '@/object-record/recor import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableNoRecordGroupBody = () => { - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, ); const isRecordTableInitialLoading = useRecoilComponentValueV2( isRecordTableInitialLoadingComponentState, ); - if (isRecordTableInitialLoading && allRowIds.length === 0) { + if (isRecordTableInitialLoading && allRecordIds.length === 0) { return ; } return ( - + - + ); }; 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 5f1b2dc77..e057d2cbd 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,17 +1,18 @@ 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 { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; 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 { RecordTableBodyRecordGroupDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider'; import { RecordTablePendingRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRow'; +import { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection'; import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; export const RecordTableRecordGroupsBody = () => { - const allRowIds = useRecoilComponentValueV2( - recordIndexAllRowIdsComponentState, + const allRecordIds = useRecoilComponentValueV2( + recordIndexAllRecordIdsComponentSelector, ); const isRecordTableInitialLoading = useRecoilComponentValueV2( @@ -22,23 +23,26 @@ export const RecordTableRecordGroupsBody = () => { visibleRecordGroupIdsComponentSelector, ); - if (isRecordTableInitialLoading && allRowIds.length === 0) { + if (isRecordTableInitialLoading && allRecordIds.length === 0) { return ; } return ( - - + + - {visibleRecordGroupIds.map((recordGroupId) => ( - - - - ))} - + {visibleRecordGroupIds.map((recordGroupId) => ( + + + + + + + ))} + ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx index da537f801..779061ecd 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/__tests__/useUpsertRecord.test.tsx @@ -4,12 +4,14 @@ import { RecoilRoot } from 'recoil'; import { createState } from 'twenty-ui'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; +import { CoreObjectNamePlural } from '@/object-metadata/types/CoreObjectNamePlural'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; import { textfieldDefinition } from '@/object-record/record-field/__mocks__/fieldDefinitions'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { useUpsertRecord } from '@/object-record/record-table/record-table-cell/hooks/useUpsertRecord'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; const draftValue = 'updated Name'; @@ -62,22 +64,26 @@ const Wrapper = ({ snapshot.set(draftValueState, draftValueMockedValue); }} > - - {children} - + + {children} + + ); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts index d1d454f93..083f244fc 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-cell/hooks/useUpsertRecord.ts @@ -4,10 +4,12 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { getLabelIdentifierFieldMetadataItem } from '@/object-metadata/utils/getLabelIdentifierFieldMetadataItem'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector'; +import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector'; import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState'; import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector'; import { isDefined } from '~/utils/isDefined'; @@ -18,8 +20,13 @@ export const useUpsertRecord = ({ objectNameSingular: string; recordTableId: string; }) => { + const hasRecordGroups = useRecoilComponentValueV2( + hasRecordGroupsComponentSelector, + ); + const { createOneRecord } = useCreateOneRecord({ objectNameSingular, + shouldMatchRootQueryFilter: hasRecordGroups, }); const recordTablePendingRecordIdState = useRecoilComponentCallbackStateV2( diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx index 6c7def1b0..940398243 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-row/components/RecordTableRowWrapper.tsx @@ -14,17 +14,19 @@ import { RecordTableWithWrappersScrollWrapperContext } from '@/ui/utilities/scro import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +type RecordTableRowWrapperProps = { + recordId: string; + rowIndex: number; + isPendingRow?: boolean; + children: ReactNode; +}; + export const RecordTableRowWrapper = ({ recordId, rowIndex, isPendingRow, children, -}: { - recordId: string; - rowIndex: number; - isPendingRow?: boolean; - children: ReactNode; -}) => { +}: RecordTableRowWrapperProps) => { const trRef = useRef(null); const { objectMetadataItem } = useContext(RecordTableContext); diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx new file mode 100644 index 000000000..4e3058011 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx @@ -0,0 +1,115 @@ +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { motion } from 'framer-motion'; +import { useCallback } from 'react'; +import { IconChevronUp, isDefined, Tag } from 'twenty-ui'; + +import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId'; +import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState'; +import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState'; +import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState'; +import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; +import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2'; +import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useRecoilValue } from 'recoil'; + +const StyledTrContainer = styled.tr` + cursor: pointer; +`; + +const StyledChevronContainer = styled.td` + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; + color: ${({ theme }) => theme.font.color.secondary}; + text-align: center; + vertical-align: middle; +`; + +const StyledTotalRow = styled.span` + color: ${({ theme }) => theme.font.color.tertiary}; + margin-left: ${({ theme }) => theme.spacing(2)}; + text-align: center; + vertical-align: middle; +`; + +const StyledRecordGroupSection = styled.td` + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; + padding-bottom: 6px; + padding-top: 6px; +`; + +const StyledEmptyTd = styled.td` + border-bottom: 1px solid ${({ theme }) => theme.border.color.light}; +`; + +export const RecordTableRecordGroupSection = () => { + const theme = useTheme(); + + const currentRecordGroupId = useCurrentRecordGroupId(); + + const visibleColumns = useRecoilComponentValueV2( + visibleTableColumnsComponentSelector, + ); + + const recordIdsByGroup = useRecoilComponentFamilyValueV2( + recordIndexRecordIdsByGroupComponentFamilyState, + currentRecordGroupId, + ); + + const [ + isRecordGroupTableSectionToggled, + setIsRecordGroupTableSectionToggled, + ] = useRecoilComponentFamilyStateV2( + isRecordGroupTableSectionToggledComponentState, + currentRecordGroupId, + ); + + const recordGroup = useRecoilValue( + recordGroupDefinitionFamilyState(currentRecordGroupId), + ); + + const handleDropdownToggle = useCallback(() => { + setIsRecordGroupTableSectionToggled((prevState) => !prevState); + }, [setIsRecordGroupTableSectionToggled]); + + if (!isDefined(recordGroup)) { + return null; + } + + return ( + + + + + + + + + + {recordIdsByGroup.length} + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState.ts b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState.ts new file mode 100644 index 000000000..c83dad1de --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState.ts @@ -0,0 +1,10 @@ +import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition'; +import { createComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentFamilyStateV2'; +import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext'; + +export const isRecordGroupTableSectionToggledComponentState = + createComponentFamilyStateV2({ + key: 'isRecordGroupTableSectionToggledComponentState', + defaultValue: true, + componentInstanceContext: ViewComponentInstanceContext, + }); 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 c9d1db2d3..0ff98a92b 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,6 +1,6 @@ import { selectedRowIdsComponentSelector } from '@/object-record/record-table/states/selectors/selectedRowIdsComponentSelector'; -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; import { AllRowsSelectedStatus } from '../../types/AllRowSelectedStatus'; @@ -12,9 +12,9 @@ export const allRowsSelectedStatusComponentSelector = get: ({ instanceId }) => ({ get }) => { - const allRowIds = get( + const allRecordIds = get( // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! - recordIndexAllRowIdsComponentState.atomFamily({ + recordIndexAllRecordIdsComponentSelector.selectorFamily({ instanceId, }), ); @@ -30,7 +30,7 @@ export const allRowsSelectedStatusComponentSelector = const allRowsSelectedStatus = numberOfSelectedRows === 0 ? 'none' - : selectedRowIds.length === allRowIds.length + : selectedRowIds.length === allRecordIds.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 734056d3f..865ce83b1 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,4 +1,4 @@ -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; @@ -11,19 +11,19 @@ export const selectedRowIdsComponentSelector = createComponentSelectorV2< get: ({ instanceId }) => ({ get }) => { - const allRowIds = get( + const allRecordIds = get( // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! - recordIndexAllRowIdsComponentState.atomFamily({ + recordIndexAllRecordIdsComponentSelector.selectorFamily({ instanceId, }), ); - return allRowIds.filter( - (rowId) => + return allRecordIds.filter( + (recordId) => get( isRowSelectedComponentFamilyState.atomFamily({ instanceId, - familyKey: rowId, + familyKey: recordId, }), ) === true, ); 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 00fcba743..e8b82ec9f 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,4 +1,4 @@ -import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState'; +import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector'; import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState'; import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext'; import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2'; @@ -11,19 +11,19 @@ export const unselectedRowIdsComponentSelector = createComponentSelectorV2< get: ({ instanceId }) => ({ get }) => { - const rowIds = get( + const allRecordIds = get( // TODO: Working because instanceId is the same, but we're not in the same context, should be changed ! - recordIndexAllRowIdsComponentState.atomFamily({ + recordIndexAllRecordIdsComponentSelector.selectorFamily({ instanceId, }), ); - return rowIds.filter( - (rowId) => + return allRecordIds.filter( + (recordId) => get( isRowSelectedComponentFamilyState.atomFamily({ instanceId, - familyKey: rowId, + familyKey: recordId, }), ) === false, ); diff --git a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts index 8f08559d7..d3a45dbfb 100644 --- a/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts +++ b/packages/twenty-front/src/modules/ui/utilities/state/component-state/utils/createComponentSelectorV2.ts @@ -14,6 +14,7 @@ import { isDefined } from 'twenty-ui'; export function createComponentSelectorV2(options: { key: string; get: SelectorGetter; + set?: never; componentInstanceContext: ComponentInstanceStateContext | null; }): ComponentReadOnlySelectorV2; diff --git a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx index 3c91c42fb..a90fe4389 100644 --- a/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx +++ b/packages/twenty-ui/src/navigation/menu-item/components/MenuItemSelect.tsx @@ -38,7 +38,7 @@ export const StyledMenuItemSelect = styled(StyledMenuItemBase)<{ `; type MenuItemSelectProps = { - LeftIcon: IconComponent | null | undefined; + LeftIcon?: IconComponent | null | undefined; selected: boolean; text: string; className?: string;