diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx index b07d5dfc6..c4bba66f7 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/components/ObjectSortDropdownButton.tsx @@ -6,7 +6,7 @@ import { useCloseSortDropdown } from '@/object-record/object-sort-dropdown/hooks import { useResetRecordSortDropdownSearchInput } from '@/object-record/object-sort-dropdown/hooks/useResetRecordSortDropdownSearchInput'; import { useResetSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useResetSortDropdown'; import { useToggleSortDropdown } from '@/object-record/object-sort-dropdown/hooks/useToggleSortDropdown'; -import { isRecordSortDirectionMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionMenuUnfoldedComponentState'; +import { isRecordSortDirectionDropdownMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState'; import { objectSortDropdownSearchInputComponentState } from '@/object-record/object-sort-dropdown/states/objectSortDropdownSearchInputComponentState'; import { onSortSelectComponentState } from '@/object-record/object-sort-dropdown/states/onSortSelectScopedState'; import { selectedRecordSortDirectionComponentState } from '@/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState'; @@ -84,7 +84,7 @@ export const ObjectSortDropdownButton = ({ ); const isRecordSortDirectionMenuUnfolded = useRecoilComponentValueV2( - isRecordSortDirectionMenuUnfoldedComponentState, + isRecordSortDirectionDropdownMenuUnfoldedComponentState, ); const { resetSortDropdown } = useResetSortDropdown(); @@ -168,7 +168,7 @@ export const ObjectSortDropdownButton = ({ useRecoilComponentStateV2(selectedRecordSortDirectionComponentState); const setIsRecordSortDirectionMenuUnfolded = useSetRecoilComponentStateV2( - isRecordSortDirectionMenuUnfoldedComponentState, + isRecordSortDirectionDropdownMenuUnfoldedComponentState, ); const handleSortDirectionClick = (sortDirection: RecordSortDirection) => { diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useResetSortDropdown.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useResetSortDropdown.ts index 2642176a5..a46bb9876 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useResetSortDropdown.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/hooks/useResetSortDropdown.ts @@ -1,18 +1,19 @@ -import { isRecordSortDirectionMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionMenuUnfoldedComponentState'; +import { isRecordSortDirectionDropdownMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState'; import { selectedRecordSortDirectionComponentState } from '@/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; export const useResetSortDropdown = () => { - const setIsRecordSortDirectionMenuUnfolded = useSetRecoilComponentStateV2( - isRecordSortDirectionMenuUnfoldedComponentState, - ); + const setIsRecordSortDirectionDropdownMenuUnfolded = + useSetRecoilComponentStateV2( + isRecordSortDirectionDropdownMenuUnfoldedComponentState, + ); const setSelectedRecordSortDirection = useSetRecoilComponentStateV2( selectedRecordSortDirectionComponentState, ); const resetSortDropdown = () => { - setIsRecordSortDirectionMenuUnfolded(false); + setIsRecordSortDirectionDropdownMenuUnfolded(false); setSelectedRecordSortDirection('asc'); }; diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionMenuUnfoldedComponentState.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState.ts similarity index 74% rename from packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionMenuUnfoldedComponentState.ts rename to packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState.ts index 26012b1b9..51c2d3ee3 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionMenuUnfoldedComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState.ts @@ -1,9 +1,9 @@ import { ObjectSortDropdownComponentInstanceContext } from '@/object-record/object-sort-dropdown/states/context/ObjectSortDropdownComponentInstanceContext'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -export const isRecordSortDirectionMenuUnfoldedComponentState = +export const isRecordSortDirectionDropdownMenuUnfoldedComponentState = createComponentStateV2({ - key: 'isRecordSortDirectionMenuUnfoldedComponentState', + key: 'isRecordSortDirectionDropdownMenuUnfoldedComponentState', defaultValue: false, componentInstanceContext: ObjectSortDropdownComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx index 9f3d0e82a..79a66702f 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexRemoveSortingModal.tsx @@ -1,5 +1,6 @@ import { useRecoilState } from 'recoil'; +import { useRemoveRecordSort } from '@/object-record/record-sort/hooks/useRemoveRecordSort'; import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { useDeleteCombinedViewSorts } from '@/views/hooks/useDeleteCombinedViewSorts'; @@ -22,9 +23,12 @@ export const RecordIndexRemoveSortingModal = ({ const { deleteCombinedViewSort } = useDeleteCombinedViewSorts(recordIndexId); + const { removeRecordSort } = useRemoveRecordSort(); + const handleRemoveClick = () => { fieldMetadataIds.forEach((id) => { deleteCombinedViewSort(id); + removeRecordSort(id); }); }; diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts index 634d0b95a..3ddcc99bd 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useHandleToggleColumnSort.ts @@ -29,7 +29,7 @@ export const useHandleToggleColumnSort = ({ const { upsertRecordSort } = useUpsertRecordSort(); const handleToggleColumnSort = useCallback( - (fieldMetadataId: string) => { + async (fieldMetadataId: string) => { const correspondingColumnDefinition = columnDefinitions.find( (columnDefinition) => columnDefinition.fieldMetadataId === fieldMetadataId, @@ -48,8 +48,9 @@ export const useHandleToggleColumnSort = ({ direction: 'asc', }; - upsertCombinedViewSort(newSort); upsertRecordSort(newSort); + + await upsertCombinedViewSort(newSort); }, [columnDefinitions, upsertCombinedViewSort, upsertRecordSort], ); diff --git a/packages/twenty-front/src/modules/object-record/record-sort/hooks/useRemoveRecordSort.ts b/packages/twenty-front/src/modules/object-record/record-sort/hooks/useRemoveRecordSort.ts new file mode 100644 index 000000000..58d410132 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-sort/hooks/useRemoveRecordSort.ts @@ -0,0 +1,48 @@ +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { useRecoilCallback } from 'recoil'; + +export const useRemoveRecordSort = () => { + const currentRecordSortsCallbackState = useRecoilComponentCallbackStateV2( + currentRecordSortsComponentState, + ); + + const removeRecordSort = useRecoilCallback( + ({ set, snapshot }) => + (fieldMetadataId: string) => { + const currentRecordSorts = getSnapshotValue( + snapshot, + currentRecordSortsCallbackState, + ); + + const hasFoundRecordSortInCurrentRecordSorts = currentRecordSorts.some( + (existingSort) => existingSort.fieldMetadataId === fieldMetadataId, + ); + + if (hasFoundRecordSortInCurrentRecordSorts) { + set(currentRecordSortsCallbackState, (currentRecordSorts) => { + const newCurrentRecordSorts = [...currentRecordSorts]; + + const indexOfSortToRemove = newCurrentRecordSorts.findIndex( + (existingSort) => + existingSort.fieldMetadataId === fieldMetadataId, + ); + + if (indexOfSortToRemove < 0) { + return newCurrentRecordSorts; + } + + newCurrentRecordSorts.splice(indexOfSortToRemove, 1); + + return newCurrentRecordSorts; + }); + } + }, + [currentRecordSortsCallbackState], + ); + + return { + removeRecordSort, + }; +}; diff --git a/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx b/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx index 8e55d117d..f829211a6 100644 --- a/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx +++ b/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx @@ -1,5 +1,6 @@ import { IconArrowDown, IconArrowUp } from 'twenty-ui'; +import { useRemoveRecordSort } from '@/object-record/record-sort/hooks/useRemoveRecordSort'; import { useUpsertRecordSort } from '@/object-record/record-sort/hooks/useUpsertRecordSort'; import { RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { SortOrFilterChip } from '@/views/components/SortOrFilterChip'; @@ -13,12 +14,15 @@ type EditableSortChipProps = { export const EditableSortChip = ({ recordSort }: EditableSortChipProps) => { const { deleteCombinedViewSort } = useDeleteCombinedViewSorts(); + const { removeRecordSort } = useRemoveRecordSort(); + const { upsertCombinedViewSort } = useUpsertCombinedViewSorts(); const { upsertRecordSort } = useUpsertRecordSort(); const handleRemoveClick = () => { deleteCombinedViewSort(recordSort.fieldMetadataId); + removeRecordSort(recordSort.fieldMetadataId); }; const handleClick = () => { diff --git a/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx b/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx index 0363c6248..54b5fbef6 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarDetails.tsx @@ -17,6 +17,7 @@ import { useCheckIsSoftDeleteFilter } from '@/object-record/record-filter/hooks/ import { currentRecordFiltersComponentState } from '@/object-record/record-filter/states/currentRecordFiltersComponentState'; import { SoftDeleteFilterChip } from '@/views/components/SoftDeleteFilterChip'; import { useApplyCurrentViewFiltersToCurrentRecordFilters } from '@/views/hooks/useApplyCurrentViewFiltersToCurrentRecordFilters'; +import { useApplyCurrentViewSortsToCurrentRecordSorts } from '@/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts'; import { useAreViewFiltersDifferentFromRecordFilters } from '@/views/hooks/useAreViewFiltersDifferentFromRecordFilters'; import { useAreViewSortsDifferentFromRecordSorts } from '@/views/hooks/useAreViewSortsDifferentFromRecordSorts'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; @@ -163,10 +164,14 @@ export const ViewBarDetails = ({ const { applyCurrentViewFiltersToCurrentRecordFilters } = useApplyCurrentViewFiltersToCurrentRecordFilters(); + const { applyCurrentViewSortsToCurrentRecordSorts } = + useApplyCurrentViewSortsToCurrentRecordSorts(); + const handleCancelClick = () => { if (isDefined(viewId)) { resetUnsavedViewStates(viewId); applyCurrentViewFiltersToCurrentRecordFilters(); + applyCurrentViewSortsToCurrentRecordSorts(); toggleSoftDeleteFilterState(false); } }; diff --git a/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyCurrentViewSortsToCurrentRecordSorts.test.tsx b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyCurrentViewSortsToCurrentRecordSorts.test.tsx new file mode 100644 index 000000000..911e129f2 --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyCurrentViewSortsToCurrentRecordSorts.test.tsx @@ -0,0 +1,196 @@ +import { act, renderHook } from '@testing-library/react'; + +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { RecordSort } from '@/object-record/record-sort/types/RecordSort'; + +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; + +import { ViewSort } from '@/views/types/ViewSort'; +import { isDefined } from 'twenty-shared'; + +import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; +import { prefetchViewsState } from '@/prefetch/states/prefetchViewsState'; + +import { View } from '@/views/types/View'; +import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; +import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; +import { mockedViewsData } from '~/testing/mock-data/views'; +import { useApplyCurrentViewSortsToCurrentRecordSorts } from '../useApplyCurrentViewSortsToCurrentRecordSorts'; + +const mockObjectMetadataItemNameSingular = 'company'; + +describe('useApplyCurrentViewSortsToCurrentRecordSorts', () => { + const mockObjectMetadataItem = generatedMockObjectMetadataItems.find( + (item) => item.nameSingular === mockObjectMetadataItemNameSingular, + ); + + if (!isDefined(mockObjectMetadataItem)) { + throw new Error( + 'Missing mock object metadata item with name singular "company"', + ); + } + + const mockFieldMetadataItem = mockObjectMetadataItem.fields.find( + (field) => field.name === 'name', + ); + + if (!isDefined(mockFieldMetadataItem)) { + throw new Error('Missing mock field metadata item with type TEXT'); + } + + const mockViewSort: ViewSort = { + __typename: 'ViewSort', + id: 'sort-1', + fieldMetadataId: mockFieldMetadataItem.id, + direction: 'asc', + }; + + const allCompaniesView = mockedViewsData[0]; + + const mockView = { + ...allCompaniesView, + viewSorts: [mockViewSort], + } satisfies View; + + it('should apply sorts from current view', () => { + const { result } = renderHook( + () => { + const { applyCurrentViewSortsToCurrentRecordSorts } = + useApplyCurrentViewSortsToCurrentRecordSorts(); + + const currentSorts = useRecoilComponentValueV2( + currentRecordSortsComponentState, + ); + + return { + applyCurrentViewSortsToCurrentRecordSorts, + currentSorts, + }; + }, + { + wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({ + apolloMocks: [], + componentInstanceId: 'instanceId', + contextStoreCurrentObjectMetadataNameSingular: + mockObjectMetadataItemNameSingular, + onInitializeRecoilSnapshot: (snapshot) => { + snapshot.set( + contextStoreCurrentViewIdComponentState.atomFamily({ + instanceId: 'instanceId', + }), + mockView.id, + ); + + snapshot.set(prefetchViewsState, [mockView]); + }, + }), + }, + ); + + act(() => { + result.current.applyCurrentViewSortsToCurrentRecordSorts(); + }); + + expect(result.current.currentSorts).toEqual([ + { + id: mockViewSort.id, + fieldMetadataId: mockViewSort.fieldMetadataId, + direction: mockViewSort.direction, + definition: { + fieldMetadataId: mockViewSort.fieldMetadataId, + iconName: mockFieldMetadataItem.icon ?? '', + label: mockFieldMetadataItem.label, + }, + } satisfies RecordSort, + ]); + }); + + it('should not apply sorts when current view is not found', () => { + const { result } = renderHook( + () => { + const { applyCurrentViewSortsToCurrentRecordSorts } = + useApplyCurrentViewSortsToCurrentRecordSorts(); + + const currentSorts = useRecoilComponentValueV2( + currentRecordSortsComponentState, + ); + + return { + applyCurrentViewSortsToCurrentRecordSorts, + currentSorts, + }; + }, + { + wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({ + apolloMocks: [], + componentInstanceId: 'instanceId', + contextStoreCurrentObjectMetadataNameSingular: + mockObjectMetadataItemNameSingular, + onInitializeRecoilSnapshot: (snapshot) => { + snapshot.set( + contextStoreCurrentViewIdComponentState.atomFamily({ + instanceId: 'instanceId', + }), + mockView.id, + ); + + snapshot.set(prefetchViewsState, []); + }, + }), + }, + ); + + act(() => { + result.current.applyCurrentViewSortsToCurrentRecordSorts(); + }); + + expect(result.current.currentSorts).toEqual([]); + }); + + it('should handle view with empty sorts', () => { + const viewWithNoSorts = { + ...mockView, + viewSorts: [], + }; + + const { result } = renderHook( + () => { + const { applyCurrentViewSortsToCurrentRecordSorts } = + useApplyCurrentViewSortsToCurrentRecordSorts(); + + const currentSorts = useRecoilComponentValueV2( + currentRecordSortsComponentState, + ); + + return { + applyCurrentViewSortsToCurrentRecordSorts, + currentSorts, + }; + }, + { + wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({ + apolloMocks: [], + componentInstanceId: 'instanceId', + contextStoreCurrentObjectMetadataNameSingular: + mockObjectMetadataItemNameSingular, + onInitializeRecoilSnapshot: (snapshot) => { + snapshot.set( + contextStoreCurrentViewIdComponentState.atomFamily({ + instanceId: 'instanceId', + }), + mockView.id, + ); + + snapshot.set(prefetchViewsState, [viewWithNoSorts]); + }, + }), + }, + ); + + act(() => { + result.current.applyCurrentViewSortsToCurrentRecordSorts(); + }); + + expect(result.current.currentSorts).toEqual([]); + }); +}); diff --git a/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyViewSortsToCurrentRecordSorts.test.tsx b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyViewSortsToCurrentRecordSorts.test.tsx new file mode 100644 index 000000000..196a5fffe --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyViewSortsToCurrentRecordSorts.test.tsx @@ -0,0 +1,110 @@ +import { act, renderHook } from '@testing-library/react'; + +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { RecordSort } from '@/object-record/record-sort/types/RecordSort'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { ViewSort } from '@/views/types/ViewSort'; +import { isDefined } from 'twenty-shared'; + +import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; + +import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; +import { useApplyViewSortsToCurrentRecordSorts } from '../useApplyViewSortsToCurrentRecordSorts'; + +const mockObjectMetadataItemNameSingular = 'company'; + +describe('useApplyViewSortsToCurrentRecordSorts', () => { + const mockObjectMetadataItem = generatedMockObjectMetadataItems.find( + (item) => item.nameSingular === mockObjectMetadataItemNameSingular, + ); + + if (!isDefined(mockObjectMetadataItem)) { + throw new Error( + `Missing mock object metadata item with name singular ${mockObjectMetadataItemNameSingular}`, + ); + } + + const mockFieldMetadataItem = mockObjectMetadataItem.fields.find( + (field) => field.name === 'name', + ); + + if (!isDefined(mockFieldMetadataItem)) { + throw new Error(`Missing mock field metadata Name`); + } + + const mockViewSort: ViewSort = { + __typename: 'ViewSort', + id: 'sort-1', + fieldMetadataId: mockFieldMetadataItem.id, + direction: 'asc', + }; + + it('should apply view sorts to current record sorts', () => { + const { result } = renderHook( + () => { + const { applyViewSortsToCurrentRecordSorts } = + useApplyViewSortsToCurrentRecordSorts(); + + const currentSorts = useRecoilComponentValueV2( + currentRecordSortsComponentState, + ); + + return { applyViewSortsToCurrentRecordSorts, currentSorts }; + }, + { + wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({ + apolloMocks: [], + componentInstanceId: 'instanceId', + contextStoreCurrentObjectMetadataNameSingular: + mockObjectMetadataItemNameSingular, + }), + }, + ); + + act(() => { + result.current.applyViewSortsToCurrentRecordSorts([mockViewSort]); + }); + + expect(result.current.currentSorts).toEqual([ + { + id: mockViewSort.id, + fieldMetadataId: mockViewSort.fieldMetadataId, + direction: mockViewSort.direction, + definition: { + fieldMetadataId: mockViewSort.fieldMetadataId, + label: mockFieldMetadataItem.label, + iconName: mockFieldMetadataItem.icon ?? '', + }, + } satisfies RecordSort, + ]); + }); + + it('should handle empty view sorts array', () => { + const { result } = renderHook( + () => { + const { applyViewSortsToCurrentRecordSorts } = + useApplyViewSortsToCurrentRecordSorts(); + + const currentSorts = useRecoilComponentValueV2( + currentRecordSortsComponentState, + ); + + return { applyViewSortsToCurrentRecordSorts, currentSorts }; + }, + { + wrapper: getJestMetadataAndApolloMocksAndActionMenuWrapper({ + apolloMocks: [], + componentInstanceId: 'instanceId', + contextStoreCurrentObjectMetadataNameSingular: + mockObjectMetadataItemNameSingular, + }), + }, + ); + + act(() => { + result.current.applyViewSortsToCurrentRecordSorts([]); + }); + + expect(result.current.currentSorts).toEqual([]); + }); +}); diff --git a/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts b/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts new file mode 100644 index 000000000..51992580f --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts @@ -0,0 +1,46 @@ +import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; +import { formatFieldMetadataItemsAsSortDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsSortDefinitions'; +import { useSortableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-sort/hooks/useSortableFieldMetadataItemsInRecordIndexContext'; +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { prefetchViewFromViewIdFamilySelector } from '@/prefetch/states/selector/prefetchViewFromViewIdFamilySelector'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; +import { useRecoilValue } from 'recoil'; + +import { isDefined } from 'twenty-shared'; + +export const useApplyCurrentViewSortsToCurrentRecordSorts = () => { + const currentViewId = useRecoilComponentValueV2( + contextStoreCurrentViewIdComponentState, + ); + + const currentView = useRecoilValue( + prefetchViewFromViewIdFamilySelector({ + viewId: currentViewId ?? '', + }), + ); + + const setCurrentRecordSorts = useSetRecoilComponentStateV2( + currentRecordSortsComponentState, + ); + + const { sortableFieldMetadataItems } = + useSortableFieldMetadataItemsInRecordIndexContext(); + + const applyCurrentViewSortsToCurrentRecordSorts = () => { + const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({ + fields: sortableFieldMetadataItems, + }); + + if (isDefined(currentView)) { + setCurrentRecordSorts( + mapViewSortsToSorts(currentView.viewSorts, sortDefinitions), + ); + } + }; + + return { + applyCurrentViewSortsToCurrentRecordSorts, + }; +}; diff --git a/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts b/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts new file mode 100644 index 000000000..f9ab2d558 --- /dev/null +++ b/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts @@ -0,0 +1,29 @@ +import { formatFieldMetadataItemsAsSortDefinitions } from '@/object-metadata/utils/formatFieldMetadataItemsAsSortDefinitions'; +import { useSortableFieldMetadataItemsInRecordIndexContext } from '@/object-record/record-sort/hooks/useSortableFieldMetadataItemsInRecordIndexContext'; +import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { ViewSort } from '@/views/types/ViewSort'; +import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; + +export const useApplyViewSortsToCurrentRecordSorts = () => { + const setCurrentRecordSorts = useSetRecoilComponentStateV2( + currentRecordSortsComponentState, + ); + + const { sortableFieldMetadataItems } = + useSortableFieldMetadataItemsInRecordIndexContext(); + + const applyViewSortsToCurrentRecordSorts = (viewSorts: ViewSort[]) => { + const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({ + fields: sortableFieldMetadataItems, + }); + + const recordSortsToApply = mapViewSortsToSorts(viewSorts, sortDefinitions); + + setCurrentRecordSorts(recordSortsToApply); + }; + + return { + applyViewSortsToCurrentRecordSorts, + }; +};