From 72da21217fea6b4436480e01d3a42300854f3289 Mon Sep 17 00:00:00 2001 From: Charles Bochet Date: Sun, 14 Sep 2025 17:17:35 +0200 Subject: [PATCH] Deprecate ViewSort typing (#14476) Continuing the work to deprecate the old ViewSorts and leverage types that are available in graphql.tsx codegen as we have migrated views to codegen --- packages/twenty-front/jest.config.mjs | 2 +- .../components/ObjectSortDropdownButton.tsx | 26 ++-- .../hooks/useResetSortDropdown.ts | 3 +- ...lectedRecordSortDirectionComponentState.ts | 6 +- .../__tests__/turnSortsIntoOrderBy.test.ts | 15 +- .../utils/turnSortsIntoOrderBy.ts | 5 +- .../hooks/useHandleToggleColumnSort.ts | 3 +- .../record-sort/types/RecordSort.ts | 4 +- .../record-sort/types/RecordSortDirection.ts | 3 - .../views/components/EditableSortChip.tsx | 13 +- .../components/ViewBarRecordSortEffect.tsx | 3 +- ...rentViewSortsToCurrentRecordSorts.test.tsx | 21 +-- ...pplyViewSortsToCurrentRecordSorts.test.tsx | 12 +- .../internal/usePersistViewSortRecords.ts | 108 ++++---------- ...plyCurrentViewSortsToCurrentRecordSorts.ts | 7 +- .../useApplyViewSortsToCurrentRecordSorts.ts | 15 +- ...useAreViewSortsDifferentFromRecordSorts.ts | 6 +- .../hooks/useCreateViewFromCurrentView.ts | 14 +- .../useRefreshCoreViewsByObjectMetadataId.ts | 3 +- .../hooks/useSaveRecordSortsToViewSorts.ts | 10 +- .../useTriggerViewSortOptimisticEffect.ts | 141 ++++++++++++++++++ .../views/types/CoreViewSortEssential.ts | 6 + .../views/types/CoreViewWithRelations.ts | 4 +- .../src/modules/views/types/GraphQLView.ts | 4 +- .../src/modules/views/types/View.ts | 4 +- .../src/modules/views/types/ViewSort.ts | 8 - .../utils/__tests__/areViewSortsEqual.test.ts | 15 +- .../__tests__/getViewSortsToCreate.test.ts | 29 ++-- .../__tests__/getViewSortsToDelete.test.ts | 42 +++--- .../__tests__/getViewSortsToUpdate.test.ts | 34 ++--- .../utils/__tests__/viewMapFunctions.test.ts | 24 --- .../modules/views/utils/areViewSortsEqual.ts | 23 ++- .../utils/convertCoreViewSortToViewSort.ts | 14 -- .../views/utils/convertCoreViewToView.ts | 5 +- .../utils/convertViewSortDirectionToCore.ts | 9 -- .../views/utils/getViewSortsToCreate.ts | 7 +- .../views/utils/getViewSortsToDelete.ts | 6 +- .../views/utils/getViewSortsToUpdate.ts | 9 +- .../views/utils/mapRecordSortToViewSort.ts | 11 +- .../views/utils/mapViewSortsToSorts.ts | 15 -- 40 files changed, 375 insertions(+), 314 deletions(-) delete mode 100644 packages/twenty-front/src/modules/object-record/record-sort/types/RecordSortDirection.ts create mode 100644 packages/twenty-front/src/modules/views/optimistic-effects/hooks/useTriggerViewSortOptimisticEffect.ts create mode 100644 packages/twenty-front/src/modules/views/types/CoreViewSortEssential.ts delete mode 100644 packages/twenty-front/src/modules/views/types/ViewSort.ts delete mode 100644 packages/twenty-front/src/modules/views/utils/convertCoreViewSortToViewSort.ts delete mode 100644 packages/twenty-front/src/modules/views/utils/convertViewSortDirectionToCore.ts delete mode 100644 packages/twenty-front/src/modules/views/utils/mapViewSortsToSorts.ts diff --git a/packages/twenty-front/jest.config.mjs b/packages/twenty-front/jest.config.mjs index f7ddd9949e..cf7dbc6fb1 100644 --- a/packages/twenty-front/jest.config.mjs +++ b/packages/twenty-front/jest.config.mjs @@ -62,7 +62,7 @@ const jestConfig = { coverageThreshold: { global: { statements: 53, - lines: 53, + lines: 52, functions: 42, }, }, 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 910cde6476..adb8c88235 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 @@ -10,10 +10,6 @@ import { selectedRecordSortDirectionComponentState } from '@/object-record/objec import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; import { useRecordIndexContextOrThrow } from '@/object-record/record-index/contexts/RecordIndexContext'; import { useUpsertRecordSort } from '@/object-record/record-sort/hooks/useUpsertRecordSort'; -import { - RECORD_SORT_DIRECTIONS, - type RecordSortDirection, -} from '@/object-record/record-sort/types/RecordSortDirection'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader'; @@ -38,6 +34,7 @@ import { findByProperty } from 'twenty-shared/utils'; import { IconX, useIcons } from 'twenty-ui/display'; import { MenuItem } from 'twenty-ui/navigation'; import { v4 } from 'uuid'; +import { ViewSortDirection } from '~/generated/graphql'; export const ObjectSortDropdownButton = () => { const { resetRecordSortDropdownSearchInput } = @@ -136,7 +133,7 @@ export const ObjectSortDropdownButton = () => { isRecordSortDirectionDropdownMenuUnfoldedComponentState, ); - const handleSortDirectionClick = (sortDirection: RecordSortDirection) => { + const handleSortDirectionClick = (sortDirection: ViewSortDirection) => { setSelectedRecordSortDirection(sortDirection); setIsRecordSortDirectionMenuUnfolded(false); }; @@ -190,21 +187,24 @@ export const ObjectSortDropdownButton = () => { ({ - value: sortDirection, - label: sortDirection === 'asc' ? t`Ascending` : t`Descending`, - }))} + options={[ViewSortDirection.ASC, ViewSortDirection.DESC].map( + (sortDirection) => ({ + value: sortDirection, + label: + sortDirection === ViewSortDirection.ASC + ? t`Ascending` + : t`Descending`, + }), + )} selectedOption={{ value: selectedRecordSortDirection, label: - selectedRecordSortDirection === 'asc' + selectedRecordSortDirection === ViewSortDirection.ASC ? t`Ascending` : t`Descending`, }} onChange={(sortDirection) => - handleSortDirectionClick( - sortDirection.value as RecordSortDirection, - ) + handleSortDirectionClick(sortDirection.value as ViewSortDirection) } widthInPixels={GenericDropdownContentWidth.ExtraLarge} /> 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 2e873783cd..8ab37fc425 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,6 +1,7 @@ import { isRecordSortDirectionDropdownMenuUnfoldedComponentState } from '@/object-record/object-sort-dropdown/states/isRecordSortDirectionDropdownMenuUnfoldedComponentState'; import { selectedRecordSortDirectionComponentState } from '@/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { ViewSortDirection } from '~/generated/graphql'; export const useResetSortDropdown = () => { const setIsRecordSortDirectionDropdownMenuUnfolded = @@ -14,7 +15,7 @@ export const useResetSortDropdown = () => { const resetSortDropdown = () => { setIsRecordSortDirectionDropdownMenuUnfolded(false); - setSelectedRecordSortDirection('asc'); + setSelectedRecordSortDirection(ViewSortDirection.ASC); }; return { diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState.ts index 11f5b0386e..29b0077a6e 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/states/selectedRecordSortDirectionComponentState.ts @@ -1,10 +1,10 @@ import { ObjectSortDropdownComponentInstanceContext } from '@/object-record/object-sort-dropdown/states/context/ObjectSortDropdownComponentInstanceContext'; -import { type RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection'; import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; +import { ViewSortDirection } from '~/generated/graphql'; export const selectedRecordSortDirectionComponentState = - createComponentState({ + createComponentState({ key: 'selectedRecordSortDirectionComponentState', - defaultValue: 'asc', + defaultValue: ViewSortDirection.ASC, componentInstanceContext: ObjectSortDropdownComponentInstanceContext, }); diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts index bd9ce8266a..e218bc54c0 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/__tests__/turnSortsIntoOrderBy.test.ts @@ -4,7 +4,10 @@ import { type RecordGqlOperationOrderBy } from '@/object-record/graphql/types/Re import { turnSortsIntoOrderBy } from '@/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy'; import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { type EachTestingContext } from 'twenty-shared/testing'; -import { FieldMetadataType } from '~/generated-metadata/graphql'; +import { + FieldMetadataType, + ViewSortDirection, +} from '~/generated-metadata/graphql'; const fields = [ { @@ -81,7 +84,7 @@ const turnSortsIntoOrderByTestUseCases: TurnSortsIntoOrderTestContext[] = [ { id: 'id', fieldMetadataId: 'field1', - direction: 'asc', + direction: ViewSortDirection.ASC, }, ], expected: [{ field1: 'AscNullsFirst' }, { position: 'AscNullsFirst' }], @@ -98,12 +101,12 @@ const turnSortsIntoOrderByTestUseCases: TurnSortsIntoOrderTestContext[] = [ { id: 'id', fieldMetadataId: 'field1', - direction: 'asc', + direction: ViewSortDirection.ASC, }, { id: 'id', fieldMetadataId: 'field2', - direction: 'desc', + direction: ViewSortDirection.DESC, }, ], expected: [ @@ -121,7 +124,7 @@ const turnSortsIntoOrderByTestUseCases: TurnSortsIntoOrderTestContext[] = [ { id: 'id', fieldMetadataId: 'invalidField', - direction: 'asc', + direction: ViewSortDirection.ASC, }, ], expected: [{ position: 'AscNullsFirst' }], @@ -135,7 +138,7 @@ const turnSortsIntoOrderByTestUseCases: TurnSortsIntoOrderTestContext[] = [ { id: 'id', fieldMetadataId: 'invalidField', - direction: 'asc', + direction: ViewSortDirection.ASC, }, ], expected: [], diff --git a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts index 99ca41b6e1..694f313c02 100644 --- a/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts +++ b/packages/twenty-front/src/modules/object-record/object-sort-dropdown/utils/turnSortsIntoOrderBy.ts @@ -10,6 +10,7 @@ import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasO import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { type OrderBy } from '@/types/OrderBy'; import { isDefined } from 'twenty-shared/utils'; +import { ViewSortDirection } from '~/generated/graphql'; export const turnSortsIntoOrderBy = ( objectMetadataItem: ObjectMetadataItem, @@ -29,7 +30,9 @@ export const turnSortsIntoOrderBy = ( } const direction: OrderBy = - sort.direction === 'asc' ? 'AscNullsFirst' : 'DescNullsLast'; + sort.direction === ViewSortDirection.ASC + ? 'AscNullsFirst' + : 'DescNullsLast'; return getOrderByForFieldMetadataType(correspondingField, direction); }) 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 dfa47ad364..2994770e12 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 @@ -6,6 +6,7 @@ import { useUpsertRecordSort } from '@/object-record/record-sort/hooks/useUpsert import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { isDefined } from 'twenty-shared/utils'; import { v4 } from 'uuid'; +import { ViewSortDirection } from '~/generated/graphql'; type UseHandleToggleColumnSortProps = { objectMetadataItemId: string; @@ -35,7 +36,7 @@ export const useHandleToggleColumnSort = ({ const newSort: RecordSort = { id: v4(), fieldMetadataId, - direction: 'asc', + direction: ViewSortDirection.ASC, }; upsertRecordSort(newSort); diff --git a/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSort.ts b/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSort.ts index 2278585116..f419ff05ee 100644 --- a/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSort.ts +++ b/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSort.ts @@ -1,7 +1,7 @@ -import { type RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection'; +import { type ViewSortDirection } from '~/generated/graphql'; export type RecordSort = { id: string; fieldMetadataId: string; - direction: RecordSortDirection; + direction: ViewSortDirection; }; diff --git a/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSortDirection.ts b/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSortDirection.ts deleted file mode 100644 index 2f65492ccb..0000000000 --- a/packages/twenty-front/src/modules/object-record/record-sort/types/RecordSortDirection.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const RECORD_SORT_DIRECTIONS = ['asc', 'desc'] as const; - -export type RecordSortDirection = (typeof RECORD_SORT_DIRECTIONS)[number]; diff --git a/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx b/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx index 6eb6701b77..3b36343e14 100644 --- a/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx +++ b/packages/twenty-front/src/modules/views/components/EditableSortChip.tsx @@ -4,6 +4,7 @@ import { useUpsertRecordSort } from '@/object-record/record-sort/hooks/useUpsert import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { SortOrFilterChip } from '@/views/components/SortOrFilterChip'; import { IconArrowDown, IconArrowUp } from 'twenty-ui/display'; +import { ViewSortDirection } from '~/generated/graphql'; type EditableSortChipProps = { recordSort: RecordSort; @@ -25,9 +26,11 @@ export const EditableSortChip = ({ recordSort }: EditableSortChipProps) => { const handleClick = () => { const newSort: RecordSort = { ...recordSort, - direction: recordSort.direction === 'asc' ? 'desc' : 'asc', + direction: + recordSort.direction === ViewSortDirection.ASC + ? ViewSortDirection.DESC + : ViewSortDirection.ASC, }; - upsertRecordSort(newSort); }; @@ -36,7 +39,11 @@ export const EditableSortChip = ({ recordSort }: EditableSortChipProps) => { key={recordSort.fieldMetadataId} testId={recordSort.fieldMetadataId} labelValue={fieldMetadataItem.label} - Icon={recordSort.direction === 'desc' ? IconArrowDown : IconArrowUp} + Icon={ + recordSort.direction === ViewSortDirection.DESC + ? IconArrowDown + : IconArrowUp + } onRemove={handleRemoveClick} onClick={handleClick} type="sort" diff --git a/packages/twenty-front/src/modules/views/components/ViewBarRecordSortEffect.tsx b/packages/twenty-front/src/modules/views/components/ViewBarRecordSortEffect.tsx index 3080605936..d265ad1eb0 100644 --- a/packages/twenty-front/src/modules/views/components/ViewBarRecordSortEffect.tsx +++ b/packages/twenty-front/src/modules/views/components/ViewBarRecordSortEffect.tsx @@ -6,7 +6,6 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { hasInitializedCurrentRecordSortsComponentFamilyState } from '@/views/states/hasInitializedCurrentRecordSortsComponentFamilyState'; import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector'; -import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { useEffect } from 'react'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -45,7 +44,7 @@ export const ViewBarRecordSortEffect = () => { } if (isDefined(currentView)) { - setCurrentRecordSorts(mapViewSortsToSorts(currentView.viewSorts)); + setCurrentRecordSorts(currentView.viewSorts); setHasInitializedCurrentRecordSorts(true); } } 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 index 0fd1b6f98c..3183f20b02 100644 --- a/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyCurrentViewSortsToCurrentRecordSorts.test.tsx +++ b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyCurrentViewSortsToCurrentRecordSorts.test.tsx @@ -1,19 +1,17 @@ import { act, renderHook } from '@testing-library/react'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; -import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { type ViewSort } from '@/views/types/ViewSort'; - import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState'; import { coreViewsState } from '@/views/states/coreViewState'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations'; import { type View } from '@/views/types/View'; import { isDefined } from 'twenty-shared/utils'; -import { ViewSortDirection, type CoreViewSort } from '~/generated/graphql'; +import { ViewSortDirection } from '~/generated/graphql'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { mockedCoreViewsData, @@ -43,24 +41,21 @@ describe('useApplyCurrentViewSortsToCurrentRecordSorts', () => { throw new Error('Missing mock field metadata item with type TEXT'); } - const mockViewSort: ViewSort = { - __typename: 'ViewSort', + const mockViewSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: mockFieldMetadataItem.id, - direction: 'asc', + direction: ViewSortDirection.ASC, + viewId: 'view-1', }; const allCompaniesView = mockedViewsData[0]; const allCompaniesCoreView = mockedCoreViewsData[0]; - const mockCoreViewSort: Omit = { - __typename: 'CoreViewSort', + const mockCoreViewSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: mockFieldMetadataItem.id, direction: ViewSortDirection.ASC, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - viewId: allCompaniesCoreView.id, + viewId: 'view-1', }; const mockView = { @@ -111,7 +106,7 @@ describe('useApplyCurrentViewSortsToCurrentRecordSorts', () => { id: mockViewSort.id, fieldMetadataId: mockViewSort.fieldMetadataId, direction: mockViewSort.direction, - } satisfies RecordSort, + }, ]); }); 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 index 442639664d..07ddb0c55e 100644 --- a/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyViewSortsToCurrentRecordSorts.test.tsx +++ b/packages/twenty-front/src/modules/views/hooks/__tests__/useApplyViewSortsToCurrentRecordSorts.test.tsx @@ -1,13 +1,13 @@ import { act, renderHook } from '@testing-library/react'; import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; -import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { type ViewSort } from '@/views/types/ViewSort'; import { generatedMockObjectMetadataItems } from '~/testing/utils/generatedMockObjectMetadataItems'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { isDefined } from 'twenty-shared/utils'; +import { ViewSortDirection } from '~/generated/graphql'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { useApplyViewSortsToCurrentRecordSorts } from '../useApplyViewSortsToCurrentRecordSorts'; @@ -32,11 +32,11 @@ describe('useApplyViewSortsToCurrentRecordSorts', () => { throw new Error(`Missing mock field metadata Name`); } - const mockViewSort: ViewSort = { - __typename: 'ViewSort', + const mockViewSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: mockFieldMetadataItem.id, - direction: 'asc', + direction: ViewSortDirection.ASC, + viewId: 'view-1', }; it('should apply view sorts to current record sorts', () => { @@ -70,7 +70,7 @@ describe('useApplyViewSortsToCurrentRecordSorts', () => { id: mockViewSort.id, fieldMetadataId: mockViewSort.fieldMetadataId, direction: mockViewSort.direction, - } satisfies RecordSort, + }, ]); }); diff --git a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts index 9ea99cf824..c915221633 100644 --- a/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts +++ b/packages/twenty-front/src/modules/views/hooks/internal/usePersistViewSortRecords.ts @@ -1,39 +1,26 @@ import { useCallback } from 'react'; -import { triggerCreateRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerCreateRecordsOptimisticEffect'; -import { triggerDestroyRecordsOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerDestroyRecordsOptimisticEffect'; -import { triggerUpdateRecordOptimisticEffect } from '@/apollo/optimistic-effect/utils/triggerUpdateRecordOptimisticEffect'; -import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache'; -import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { CREATE_CORE_VIEW_SORT } from '@/views/graphql/mutations/createCoreViewSort'; import { DESTROY_CORE_VIEW_SORT } from '@/views/graphql/mutations/destroyCoreViewSort'; import { UPDATE_CORE_VIEW_SORT } from '@/views/graphql/mutations/updateCoreViewSort'; +import { useTriggerViewSortOptimisticEffect } from '@/views/optimistic-effects/hooks/useTriggerViewSortOptimisticEffect'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { type GraphQLView } from '@/views/types/GraphQLView'; -import { type ViewSort } from '@/views/types/ViewSort'; -import { convertViewSortDirectionToCore } from '@/views/utils/convertViewSortDirectionToCore'; import { useApolloClient } from '@apollo/client'; -import { isNull } from '@sniptt/guards'; import { isDefined } from 'twenty-shared/utils'; import { type CoreViewSort } from '~/generated/graphql'; export const usePersistViewSortRecords = () => { - const { objectMetadataItem } = useObjectMetadataItem({ - objectNameSingular: CoreObjectNameSingular.ViewSort, - }); - - const getRecordFromCache = useGetRecordFromCache({ - objectNameSingular: CoreObjectNameSingular.ViewSort, - }); - - const { objectMetadataItems } = useObjectMetadataItems(); - const { objectPermissionsByObjectMetadataId } = useObjectPermissions(); const apolloClient = useApolloClient(); + const { triggerViewSortOptimisticEffect } = + useTriggerViewSortOptimisticEffect(); + const createCoreViewSortRecords = useCallback( - (viewSortsToCreate: ViewSort[], view: Pick) => { + ( + viewSortsToCreate: CoreViewSortEssential[], + view: Pick, + ) => { if (!viewSortsToCreate.length) return; return Promise.all( viewSortsToCreate.map((viewSort) => @@ -44,35 +31,26 @@ export const usePersistViewSortRecords = () => { id: viewSort.id, fieldMetadataId: viewSort.fieldMetadataId, viewId: view.id, - direction: convertViewSortDirectionToCore(viewSort.direction), + direction: viewSort.direction, } satisfies Partial, }, - update: (cache, { data }) => { - const record = data?.['createCoreViewSort']; - if (!isDefined(record)) return; + update: (_cache, { data }) => { + const createdViewSort = data?.createCoreViewSort; + if (!isDefined(createdViewSort)) return; - triggerCreateRecordsOptimisticEffect({ - cache, - objectMetadataItem, - recordsToCreate: [record], - objectMetadataItems, - objectPermissionsByObjectMetadataId, + triggerViewSortOptimisticEffect({ + createdViewSorts: [createdViewSort], }); }, }), ), ); }, - [ - apolloClient, - objectMetadataItem, - objectMetadataItems, - objectPermissionsByObjectMetadataId, - ], + [apolloClient, triggerViewSortOptimisticEffect], ); const updateCoreViewSortRecords = useCallback( - (viewSortsToUpdate: ViewSort[]) => { + (viewSortsToUpdate: CoreViewSortEssential[]) => { if (!viewSortsToUpdate.length) return; return Promise.all( viewSortsToUpdate.map((viewSort) => @@ -81,66 +59,44 @@ export const usePersistViewSortRecords = () => { variables: { id: viewSort.id, input: { - direction: convertViewSortDirectionToCore(viewSort.direction), + direction: viewSort.direction, } satisfies Partial, }, - update: (cache, { data }) => { - const record = data?.['updateCoreViewSort']; - if (!isDefined(record)) return; + update: (_cache, { data }) => { + const updatedViewSort = data?.updateCoreViewSort; + if (!isDefined(updatedViewSort)) return; - const cachedRecord = getRecordFromCache( - record.id, - cache, - ); - if (isNull(cachedRecord)) return; - - triggerUpdateRecordOptimisticEffect({ - cache, - objectMetadataItem, - currentRecord: cachedRecord, - updatedRecord: record, - objectMetadataItems, + triggerViewSortOptimisticEffect({ + updatedViewSorts: [updatedViewSort], }); }, }), ), ); }, - [apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems], + [apolloClient, triggerViewSortOptimisticEffect], ); const deleteCoreViewSortRecords = useCallback( - (viewSortIdsToDelete: string[]) => { - if (!viewSortIdsToDelete.length) return; + (viewSortsToDelete: Pick[]) => { + if (!viewSortsToDelete.length) return; return Promise.all( - viewSortIdsToDelete.map((viewSortId) => + viewSortsToDelete.map((viewSort) => apolloClient.mutate({ mutation: DESTROY_CORE_VIEW_SORT, variables: { - id: viewSortId, + id: viewSort.id, }, - update: (cache, { data }) => { - const record = data?.['destroyCoreViewSort']; - if (!isDefined(record)) return; - - const cachedRecord = getRecordFromCache( - record.id, - cache, - ); - if (isNull(cachedRecord)) return; - - triggerDestroyRecordsOptimisticEffect({ - cache, - objectMetadataItem, - recordsToDestroy: [cachedRecord], - objectMetadataItems, + update: (_cache) => { + triggerViewSortOptimisticEffect({ + deletedViewSorts: [viewSort], }); }, }), ), ); }, - [apolloClient, getRecordFromCache, objectMetadataItem, objectMetadataItems], + [apolloClient, triggerViewSortOptimisticEffect], ); return { diff --git a/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts b/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts index 6e41a7ed89..d3fcb505a1 100644 --- a/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts +++ b/packages/twenty-front/src/modules/views/hooks/useApplyCurrentViewSortsToCurrentRecordSorts.ts @@ -3,7 +3,6 @@ import { currentRecordSortsComponentState } from '@/object-record/record-sort/st import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector'; -import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -24,7 +23,11 @@ export const useApplyCurrentViewSortsToCurrentRecordSorts = () => { const applyCurrentViewSortsToCurrentRecordSorts = () => { if (isDefined(currentView)) { - setCurrentRecordSorts(mapViewSortsToSorts(currentView.viewSorts)); + const recordSorts = currentView.viewSorts.map((viewSort) => { + const { viewId: _viewId, ...recordSort } = viewSort; + return recordSort; + }); + setCurrentRecordSorts(recordSorts); } }; diff --git a/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts b/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts index 1d274f2af2..3dbe33946c 100644 --- a/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts +++ b/packages/twenty-front/src/modules/views/hooks/useApplyViewSortsToCurrentRecordSorts.ts @@ -1,17 +1,20 @@ import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { type ViewSort } from '@/views/types/ViewSort'; -import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; export const useApplyViewSortsToCurrentRecordSorts = () => { const setCurrentRecordSorts = useSetRecoilComponentState( currentRecordSortsComponentState, ); - const applyViewSortsToCurrentRecordSorts = (viewSorts: ViewSort[]) => { - const recordSortsToApply = mapViewSortsToSorts(viewSorts); - - setCurrentRecordSorts(recordSortsToApply); + const applyViewSortsToCurrentRecordSorts = ( + viewSorts: CoreViewSortEssential[], + ) => { + const recordSorts = viewSorts.map((viewSort) => { + const { viewId: _viewId, ...recordSort } = viewSort; + return recordSort; + }); + setCurrentRecordSorts(recordSorts); }; return { diff --git a/packages/twenty-front/src/modules/views/hooks/useAreViewSortsDifferentFromRecordSorts.ts b/packages/twenty-front/src/modules/views/hooks/useAreViewSortsDifferentFromRecordSorts.ts index 5e7d866a06..9e5c7d2cae 100644 --- a/packages/twenty-front/src/modules/views/hooks/useAreViewSortsDifferentFromRecordSorts.ts +++ b/packages/twenty-front/src/modules/views/hooks/useAreViewSortsDifferentFromRecordSorts.ts @@ -6,6 +6,7 @@ import { getViewSortsToDelete } from '@/views/utils/getViewSortsToDelete'; import { getViewSortsToUpdate } from '@/views/utils/getViewSortsToUpdate'; import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort'; import { useMemo } from 'react'; +import { isDefined } from 'twenty-shared/utils'; export const useAreViewSortsDifferentFromRecordSorts = () => { const { currentView } = useGetCurrentViewOnly(); @@ -15,8 +16,11 @@ export const useAreViewSortsDifferentFromRecordSorts = () => { const viewSortsAreDifferentFromRecordSorts = useMemo(() => { const currentViewSorts = currentView?.viewSorts ?? []; + if (!isDefined(currentView)) { + return false; + } const viewSortsFromCurrentRecordSorts = currentRecordSorts.map( - mapRecordSortToViewSort, + (recordSort) => mapRecordSortToViewSort(recordSort, currentView.id), ); const viewSortsToCreate = getViewSortsToCreate( diff --git a/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts b/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts index 09e510e5d3..9205270ae0 100644 --- a/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts +++ b/packages/twenty-front/src/modules/views/hooks/useCreateViewFromCurrentView.ts @@ -17,7 +17,6 @@ import { isPersistingViewFieldsState } from '@/views/states/isPersistingViewFiel import { coreViewFromViewIdFamilySelector } from '@/views/states/selectors/coreViewFromViewIdFamilySelector'; import { type GraphQLView } from '@/views/types/GraphQLView'; import { type ViewGroup } from '@/views/types/ViewGroup'; -import { type ViewSort } from '@/views/types/ViewSort'; import { ViewType } from '@/views/types/ViewType'; import { convertViewOpenRecordInToCore } from '@/views/utils/convertViewOpenRecordInToCore'; import { convertViewTypeToCore } from '@/views/utils/convertViewTypeToCore'; @@ -206,14 +205,11 @@ export const useCreateViewFromCurrentView = (viewBarComponentId?: string) => { }); const viewSortsToCreate = currentRecordSorts - .map(mapRecordSortToViewSort) - .map( - (viewSort) => - ({ - ...viewSort, - id: v4(), - }) satisfies ViewSort, - ); + .map((recordSort) => mapRecordSortToViewSort(recordSort, newViewId)) + .map((viewSort) => ({ + ...viewSort, + id: v4(), + })); await createViewFilterGroupRecords(viewFilterGroupsToCreate, { id: newViewId, diff --git a/packages/twenty-front/src/modules/views/hooks/useRefreshCoreViewsByObjectMetadataId.ts b/packages/twenty-front/src/modules/views/hooks/useRefreshCoreViewsByObjectMetadataId.ts index 39d1297ddf..7d159794a4 100644 --- a/packages/twenty-front/src/modules/views/hooks/useRefreshCoreViewsByObjectMetadataId.ts +++ b/packages/twenty-front/src/modules/views/hooks/useRefreshCoreViewsByObjectMetadataId.ts @@ -9,7 +9,6 @@ import { getFilterableFieldsWithVectorSearch } from '@/views/utils/getFilterable import { mapViewFieldToRecordField } from '@/views/utils/mapViewFieldToRecordField'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; -import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { useRecoilCallback } from 'recoil'; import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils'; import { useFindManyCoreViewsLazyQuery } from '~/generated/graphql'; @@ -121,7 +120,7 @@ export const useRefreshCoreViewsByObjectMetadataId = () => { view.id, ), }), - mapViewSortsToSorts(view.viewSorts), + view.viewSorts, ); } } diff --git a/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts b/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts index ca0f33af92..eb5a196961 100644 --- a/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts +++ b/packages/twenty-front/src/modules/views/hooks/useSaveRecordSortsToViewSorts.ts @@ -37,7 +37,9 @@ export const useSaveRecordSortsToViewSorts = () => { currentRecordSortsCallbackState, ); - const newViewSorts = currentRecordSorts.map(mapRecordSortToViewSort); + const newViewSorts = currentRecordSorts.map((recordSort) => + mapRecordSortToViewSort(recordSort, currentView.id), + ); const viewSortsToCreate = getViewSortsToCreate( currentViewSorts, @@ -54,13 +56,9 @@ export const useSaveRecordSortsToViewSorts = () => { newViewSorts, ); - const viewSortIdsToDelete = viewSortsToDelete.map( - (viewSort) => viewSort.id, - ); - await createViewSortRecords(viewSortsToCreate, currentView); await updateViewSortRecords(viewSortsToUpdate); - await deleteViewSortRecords(viewSortIdsToDelete); + await deleteViewSortRecords(viewSortsToDelete); }, [ currentView, diff --git a/packages/twenty-front/src/modules/views/optimistic-effects/hooks/useTriggerViewSortOptimisticEffect.ts b/packages/twenty-front/src/modules/views/optimistic-effects/hooks/useTriggerViewSortOptimisticEffect.ts new file mode 100644 index 0000000000..72772f9410 --- /dev/null +++ b/packages/twenty-front/src/modules/views/optimistic-effects/hooks/useTriggerViewSortOptimisticEffect.ts @@ -0,0 +1,141 @@ +import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; +import { coreViewsState } from '@/views/states/coreViewState'; +import { type CoreViewWithRelations } from '@/views/types/CoreViewWithRelations'; +import { useApolloClient } from '@apollo/client'; +import { useRecoilCallback } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { type CoreViewSort } from '~/generated/graphql'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; + +export const useTriggerViewSortOptimisticEffect = () => { + const apolloClient = useApolloClient(); + + const cache = apolloClient.cache; + + const triggerViewSortOptimisticEffect = useRecoilCallback( + ({ set, snapshot }) => + ({ + createdViewSorts = [], + updatedViewSorts = [], + deletedViewSorts = [], + }: { + createdViewSorts?: Omit[]; + updatedViewSorts?: Omit[]; + deletedViewSorts?: Pick[]; + }) => { + const coreViews = getSnapshotValue(snapshot, coreViewsState); + let newCoreViews = [...coreViews]; + + createdViewSorts.forEach((createdViewSort) => { + cache.modify({ + id: cache.identify({ + __typename: 'CoreView', + id: createdViewSort.viewId, + }), + fields: { + viewSorts: (existingViewSorts, { toReference }) => [ + ...(existingViewSorts ?? []), + toReference(createdViewSort), + ], + }, + }); + const toBeModifiedCoreView = newCoreViews.find( + (coreView) => coreView.id === createdViewSort.viewId, + ); + if (isDefined(toBeModifiedCoreView)) { + newCoreViews = [ + ...newCoreViews.filter( + (coreView) => coreView.id !== createdViewSort.viewId, + ), + { + ...toBeModifiedCoreView, + viewSorts: [...toBeModifiedCoreView.viewSorts, createdViewSort], + }, + ]; + } + }); + + updatedViewSorts.forEach((updatedViewSort) => { + cache.modify({ + id: cache.identify({ + __typename: 'CoreView', + id: updatedViewSort.viewId, + }), + fields: { + viewSorts: (existingViewSorts, { readField, toReference }) => + existingViewSorts.map((viewSort) => { + const viewSortId = readField('id', viewSort); + if (viewSortId === updatedViewSort.id) { + return toReference(updatedViewSort); + } + return viewSort; + }), + }, + }); + const toBeModifiedCoreView = newCoreViews.find( + (coreView) => coreView.id === updatedViewSort.viewId, + ); + if (isDefined(toBeModifiedCoreView)) { + newCoreViews = [ + ...newCoreViews.filter( + (coreView) => coreView.id !== updatedViewSort.viewId, + ), + { + ...toBeModifiedCoreView, + viewSorts: [ + ...toBeModifiedCoreView.viewSorts.filter( + (viewSort) => viewSort.id !== updatedViewSort.id, + ), + updatedViewSort, + ], + }, + ]; + } + }); + + deletedViewSorts.forEach( + (deletedViewSort: Pick) => { + cache.modify({ + id: cache.identify({ + __typename: 'CoreView', + id: deletedViewSort.viewId, + }), + fields: { + viewSorts: (existingViewSorts, { readField }) => + existingViewSorts.filter( + (viewSort) => + readField('id', viewSort) !== deletedViewSort.id, + ), + }, + }); + const toBeModifiedCoreView = newCoreViews.find( + (coreView) => coreView.id === deletedViewSort.viewId, + ); + + if (isDefined(toBeModifiedCoreView)) { + newCoreViews = [ + ...newCoreViews.filter( + (coreView) => coreView.id !== deletedViewSort.viewId, + ), + { + ...toBeModifiedCoreView, + viewSorts: toBeModifiedCoreView.viewSorts.filter( + (viewSort) => viewSort.id !== deletedViewSort.id, + ), + }, + ]; + } + }, + ); + + if (!isDeeplyEqual(coreViews, newCoreViews)) { + set(coreViewsState, newCoreViews); + } + }, + [cache], + ); + + return { + triggerViewSortOptimisticEffect, + }; +}; diff --git a/packages/twenty-front/src/modules/views/types/CoreViewSortEssential.ts b/packages/twenty-front/src/modules/views/types/CoreViewSortEssential.ts new file mode 100644 index 0000000000..10895317ec --- /dev/null +++ b/packages/twenty-front/src/modules/views/types/CoreViewSortEssential.ts @@ -0,0 +1,6 @@ +import { type CoreViewSort } from '~/generated/graphql'; + +export type CoreViewSortEssential = Pick< + CoreViewSort, + 'id' | 'fieldMetadataId' | 'direction' | 'viewId' +>; diff --git a/packages/twenty-front/src/modules/views/types/CoreViewWithRelations.ts b/packages/twenty-front/src/modules/views/types/CoreViewWithRelations.ts index 585a19d89f..c45c1f6a1e 100644 --- a/packages/twenty-front/src/modules/views/types/CoreViewWithRelations.ts +++ b/packages/twenty-front/src/modules/views/types/CoreViewWithRelations.ts @@ -1,10 +1,10 @@ +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { type AggregateOperations, type CoreViewField, type CoreViewFilter, type CoreViewFilterGroup, type CoreViewGroup, - type CoreViewSort, type ViewKey, type ViewOpenRecordIn, type ViewType, @@ -27,7 +27,7 @@ export type CoreViewWithRelations = { CoreViewFilterGroup, 'workspaceId' | 'createdAt' | 'updatedAt' >[]; - viewSorts: Omit[]; + viewSorts: CoreViewSortEssential[]; kanbanAggregateOperation?: AggregateOperations | null; kanbanAggregateOperationFieldMetadataId?: string | null; position: number; diff --git a/packages/twenty-front/src/modules/views/types/GraphQLView.ts b/packages/twenty-front/src/modules/views/types/GraphQLView.ts index 9912be8a5a..3d444929a4 100644 --- a/packages/twenty-front/src/modules/views/types/GraphQLView.ts +++ b/packages/twenty-front/src/modules/views/types/GraphQLView.ts @@ -1,11 +1,11 @@ import { type AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { type ViewField } from '@/views/types/ViewField'; import { type ViewFilter } from '@/views/types/ViewFilter'; import { type ViewFilterGroup } from '@/views/types/ViewFilterGroup'; import { type ViewGroup } from '@/views/types/ViewGroup'; import { type ViewKey } from '@/views/types/ViewKey'; import { type ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; -import { type ViewSort } from '@/views/types/ViewSort'; import { type ViewType } from '@/views/types/ViewType'; export type GraphQLView = { @@ -25,7 +25,7 @@ export type GraphQLView = { viewFields: ViewField[]; viewFilters: ViewFilter[]; viewFilterGroups?: ViewFilterGroup[]; - viewSorts: ViewSort[]; + viewSorts: CoreViewSortEssential[]; viewGroups: ViewGroup[]; position: number; icon: string; diff --git a/packages/twenty-front/src/modules/views/types/View.ts b/packages/twenty-front/src/modules/views/types/View.ts index becdb29077..7e335fe52d 100644 --- a/packages/twenty-front/src/modules/views/types/View.ts +++ b/packages/twenty-front/src/modules/views/types/View.ts @@ -1,11 +1,11 @@ import { type AggregateOperations } from '@/object-record/record-table/constants/AggregateOperations'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { type ViewField } from '@/views/types/ViewField'; import { type ViewFilter } from '@/views/types/ViewFilter'; import { type ViewFilterGroup } from '@/views/types/ViewFilterGroup'; import { type ViewGroup } from '@/views/types/ViewGroup'; import { type ViewKey } from '@/views/types/ViewKey'; import { type ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; -import { type ViewSort } from '@/views/types/ViewSort'; import { type ViewType } from '@/views/types/ViewType'; export type View = { @@ -19,7 +19,7 @@ export type View = { viewGroups: ViewGroup[]; viewFilters: ViewFilter[]; viewFilterGroups?: ViewFilterGroup[]; - viewSorts: ViewSort[]; + viewSorts: CoreViewSortEssential[]; /** * @deprecated Use `viewGroups.fieldMetadataId` instead. */ diff --git a/packages/twenty-front/src/modules/views/types/ViewSort.ts b/packages/twenty-front/src/modules/views/types/ViewSort.ts deleted file mode 100644 index 3484d86cbf..0000000000 --- a/packages/twenty-front/src/modules/views/types/ViewSort.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { type RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection'; - -export type ViewSort = { - __typename: 'ViewSort'; - id: string; - fieldMetadataId: string; - direction: RecordSortDirection; -}; diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/areViewSortsEqual.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/areViewSortsEqual.test.ts index 02d348f4df..7be6408575 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/areViewSortsEqual.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/areViewSortsEqual.test.ts @@ -1,13 +1,16 @@ -import { type RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection'; -import { type ViewSort } from '@/views/types/ViewSort'; import { areViewSortsEqual } from '@/views/utils/areViewSortsEqual'; +import { type CoreViewSort, ViewSortDirection } from '~/generated/graphql'; describe('areViewSortsEqual', () => { - const baseSort: ViewSort = { - __typename: 'ViewSort', + const baseSort: CoreViewSort = { + __typename: 'CoreViewSort', id: 'sort-1', fieldMetadataId: 'field-1', - direction: 'asc', + direction: ViewSortDirection.ASC, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + viewId: 'view-1', + workspaceId: 'workspace-1', }; it('should return true when all comparable properties are equal', () => { @@ -19,7 +22,7 @@ describe('areViewSortsEqual', () => { it('should return false when displayValue is different', () => { const sortA = { ...baseSort }; - const sortB = { ...baseSort, direction: 'desc' as RecordSortDirection }; + const sortB = { ...baseSort, direction: 'desc' as ViewSortDirection }; expect(areViewSortsEqual(sortA, sortB)).toBe(false); }); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToCreate.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToCreate.test.ts index e4c2af1a8b..100b2fc7a7 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToCreate.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToCreate.test.ts @@ -1,23 +1,24 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; +import { ViewSortDirection } from '~/generated/graphql'; import { getViewSortsToCreate } from '../getViewSortsToCreate'; describe('getViewSortsToCreate', () => { - const baseSort: ViewSort = { - __typename: 'ViewSort', + const baseSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: 'field-1', - direction: 'asc', + direction: ViewSortDirection.ASC, + viewId: 'view-1', }; it('should return all sorts when current sorts array is empty', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = [ + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = [ { ...baseSort }, { ...baseSort, id: 'sort-2', fieldMetadataId: 'field-2', - } satisfies ViewSort, + } satisfies CoreViewSortEssential, ]; const result = getViewSortsToCreate(currentViewSorts, newViewSorts); @@ -26,8 +27,8 @@ describe('getViewSortsToCreate', () => { }); it('should return empty array when new sorts array is empty', () => { - const currentViewSorts: ViewSort[] = [baseSort]; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = [baseSort]; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToCreate(currentViewSorts, newViewSorts); @@ -40,11 +41,11 @@ describe('getViewSortsToCreate', () => { ...baseSort, id: 'sort-2', fieldMetadataId: 'field-2', - } satisfies ViewSort; + } satisfies CoreViewSortEssential; - const currentViewSorts: ViewSort[] = [existingSort]; + const currentViewSorts: CoreViewSortEssential[] = [existingSort]; - const newViewSorts: ViewSort[] = [ + const newViewSorts: CoreViewSortEssential[] = [ existingSort, newSortWithDifferentFieldMetadataId, ]; @@ -55,8 +56,8 @@ describe('getViewSortsToCreate', () => { }); it('should handle empty arrays for both inputs', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToCreate(currentViewSorts, newViewSorts); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToDelete.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToDelete.test.ts index 4fa61878ff..a64d651b9f 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToDelete.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToDelete.test.ts @@ -1,17 +1,18 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; +import { ViewSortDirection } from '~/generated/graphql'; import { getViewSortsToDelete } from '../getViewSortsToDelete'; describe('getViewSortsToDelete', () => { - const baseSort: ViewSort = { - __typename: 'ViewSort', + const baseSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: 'field-1', - direction: 'asc', + direction: ViewSortDirection.ASC, + viewId: 'view-1', }; it('should return empty array when current sorts array is empty', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = [baseSort]; + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = [baseSort]; const result = getViewSortsToDelete(currentViewSorts, newViewSorts); @@ -20,8 +21,8 @@ describe('getViewSortsToDelete', () => { it('should return all current sorts when new sorts array is empty', () => { const existingSort = { ...baseSort }; - const currentViewSorts: ViewSort[] = [existingSort]; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = [existingSort]; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToDelete(currentViewSorts, newViewSorts); @@ -32,12 +33,15 @@ describe('getViewSortsToDelete', () => { const sortToDelete = { ...baseSort }; const sortToKeep = { ...baseSort, - id: 'filter-2', + id: 'sort-2', fieldMetadataId: 'field-2', - } satisfies ViewSort; + }; - const currentViewSorts: ViewSort[] = [sortToDelete, sortToKeep]; - const newViewSorts: ViewSort[] = [sortToKeep]; + const currentViewSorts: CoreViewSortEssential[] = [ + sortToDelete, + sortToKeep, + ]; + const newViewSorts: CoreViewSortEssential[] = [sortToKeep]; const result = getViewSortsToDelete(currentViewSorts, newViewSorts); @@ -45,8 +49,8 @@ describe('getViewSortsToDelete', () => { }); it('should handle empty arrays for both inputs', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToDelete(currentViewSorts, newViewSorts); @@ -56,14 +60,14 @@ describe('getViewSortsToDelete', () => { it('should not delete sorts that match in both fieldMetadataId and direction', () => { const existingSort = { ...baseSort }; const matchingSort = { - __typename: 'ViewSort', id: 'sort-2', fieldMetadataId: 'field-1', - direction: 'asc', - } satisfies ViewSort; + direction: ViewSortDirection.ASC, + viewId: 'view-1', + }; - const currentViewSorts: ViewSort[] = [existingSort]; - const newViewSorts: ViewSort[] = [matchingSort]; + const currentViewSorts: CoreViewSortEssential[] = [existingSort]; + const newViewSorts: CoreViewSortEssential[] = [matchingSort]; const result = getViewSortsToDelete(currentViewSorts, newViewSorts); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToUpdate.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToUpdate.test.ts index 33bc7387ef..ac2fc0d0eb 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToUpdate.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/getViewSortsToUpdate.test.ts @@ -1,18 +1,18 @@ -import { type RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection'; -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; +import { ViewSortDirection } from '~/generated/graphql'; import { getViewSortsToUpdate } from '../getViewSortsToUpdate'; describe('getViewSortsToUpdate', () => { - const baseSort: ViewSort = { - __typename: 'ViewSort', + const baseSort: CoreViewSortEssential = { id: 'sort-1', fieldMetadataId: 'field-1', - direction: 'asc' as RecordSortDirection, + direction: ViewSortDirection.ASC, + viewId: 'view-1', }; it('should return empty array when current sorts array is empty', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = [baseSort]; + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = [baseSort]; const result = getViewSortsToUpdate(currentViewSorts, newViewSorts); @@ -20,8 +20,8 @@ describe('getViewSortsToUpdate', () => { }); it('should return empty array when new sorts array is empty', () => { - const currentViewSorts: ViewSort[] = [baseSort]; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = [baseSort]; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToUpdate(currentViewSorts, newViewSorts); @@ -32,11 +32,11 @@ describe('getViewSortsToUpdate', () => { const existingSort = { ...baseSort }; const updatedSort = { ...baseSort, - direction: 'desc', - } satisfies ViewSort; + direction: ViewSortDirection.DESC, + } satisfies CoreViewSortEssential; - const currentViewSorts: ViewSort[] = [existingSort]; - const newViewSorts: ViewSort[] = [updatedSort]; + const currentViewSorts: CoreViewSortEssential[] = [existingSort]; + const newViewSorts: CoreViewSortEssential[] = [updatedSort]; const result = getViewSortsToUpdate(currentViewSorts, newViewSorts); @@ -47,8 +47,8 @@ describe('getViewSortsToUpdate', () => { const existingSort = { ...baseSort }; const sameSort = { ...baseSort }; - const currentViewSorts: ViewSort[] = [existingSort]; - const newViewSorts: ViewSort[] = [sameSort]; + const currentViewSorts: CoreViewSortEssential[] = [existingSort]; + const newViewSorts: CoreViewSortEssential[] = [sameSort]; const result = getViewSortsToUpdate(currentViewSorts, newViewSorts); @@ -56,8 +56,8 @@ describe('getViewSortsToUpdate', () => { }); it('should handle empty arrays for both inputs', () => { - const currentViewSorts: ViewSort[] = []; - const newViewSorts: ViewSort[] = []; + const currentViewSorts: CoreViewSortEssential[] = []; + const newViewSorts: CoreViewSortEssential[] = []; const result = getViewSortsToUpdate(currentViewSorts, newViewSorts); diff --git a/packages/twenty-front/src/modules/views/utils/__tests__/viewMapFunctions.test.ts b/packages/twenty-front/src/modules/views/utils/__tests__/viewMapFunctions.test.ts index fd6afad884..ee9f1949c7 100644 --- a/packages/twenty-front/src/modules/views/utils/__tests__/viewMapFunctions.test.ts +++ b/packages/twenty-front/src/modules/views/utils/__tests__/viewMapFunctions.test.ts @@ -1,14 +1,11 @@ import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata'; import { type RecordFilter } from '@/object-record/record-filter/types/RecordFilter'; -import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; import { type ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { type ViewField } from '@/views/types/ViewField'; import { type ViewFilter } from '@/views/types/ViewFilter'; -import { type ViewSort } from '@/views/types/ViewSort'; import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField'; import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions'; import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters'; -import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts'; import { ViewFilterOperand } from 'twenty-shared/types'; import { FieldMetadataType } from '~/generated-metadata/graphql'; @@ -22,27 +19,6 @@ const baseFieldMetadataItem = { type: FieldMetadataType.FULL_NAME, }; -describe('mapViewSortsToSorts', () => { - it('should map each ViewSort object to a corresponding Sort object', () => { - const viewSorts: ViewSort[] = [ - { - __typename: 'ViewSort', - id: 'id', - fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482', - direction: 'asc', - }, - ]; - const expectedSorts: RecordSort[] = [ - { - id: 'id', - fieldMetadataId: '05731f68-6e7a-4903-8374-c0b6a9063482', - direction: 'asc', - }, - ]; - expect(mapViewSortsToSorts(viewSorts)).toEqual(expectedSorts); - }); -}); - describe('mapViewFiltersToFilters', () => { it('should map each ViewFilter object to a corresponding Filter object', () => { const viewFilters: ViewFilter[] = [ diff --git a/packages/twenty-front/src/modules/views/utils/areViewSortsEqual.ts b/packages/twenty-front/src/modules/views/utils/areViewSortsEqual.ts index fc6bf4f754..2924680cd9 100644 --- a/packages/twenty-front/src/modules/views/utils/areViewSortsEqual.ts +++ b/packages/twenty-front/src/modules/views/utils/areViewSortsEqual.ts @@ -1,16 +1,23 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSort } from '~/generated/graphql'; import { compareStrictlyExceptForNullAndUndefined } from '~/utils/compareStrictlyExceptForNullAndUndefined'; -export const areViewSortsEqual = (viewSortA: ViewSort, viewSortB: ViewSort) => { - const propertiesToCompare: (keyof ViewSort)[] = [ - 'fieldMetadataId', - 'direction', - ]; +export const areViewSortsEqual = ( + viewSortA: Pick, + viewSortB: Pick, +) => { + const propertiesToCompare: (keyof Pick< + CoreViewSort, + 'fieldMetadataId' | 'direction' + >)[] = ['fieldMetadataId', 'direction']; return propertiesToCompare.every((property) => compareStrictlyExceptForNullAndUndefined( - viewSortA[property], - viewSortB[property], + viewSortA[ + property as keyof Pick + ], + viewSortB[ + property as keyof Pick + ], ), ); }; diff --git a/packages/twenty-front/src/modules/views/utils/convertCoreViewSortToViewSort.ts b/packages/twenty-front/src/modules/views/utils/convertCoreViewSortToViewSort.ts deleted file mode 100644 index 6d82f7c662..0000000000 --- a/packages/twenty-front/src/modules/views/utils/convertCoreViewSortToViewSort.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { type ViewSort } from '@/views/types/ViewSort'; -import { type CoreViewSort, ViewSortDirection } from '~/generated/graphql'; - -export const convertCoreViewSortToViewSort = ( - coreViewSort: Pick, -): ViewSort => { - return { - __typename: 'ViewSort', - id: coreViewSort.id, - fieldMetadataId: coreViewSort.fieldMetadataId, - direction: - coreViewSort.direction === ViewSortDirection.ASC ? 'asc' : 'desc', - }; -}; diff --git a/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts b/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts index cac2d538e5..c88d01c644 100644 --- a/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts +++ b/packages/twenty-front/src/modules/views/utils/convertCoreViewToView.ts @@ -6,7 +6,6 @@ import { convertCoreViewFilterToViewFilter } from '@/views/utils/convertCoreView import { convertCoreViewGroupToViewGroup } from '@/views/utils/convertCoreViewGroupToViewGroup'; import { convertCoreViewKeyToViewKey } from '@/views/utils/convertCoreViewKeyToViewKey'; import { convertCoreViewOpenRecordInToViewOpenRecordIn } from '@/views/utils/convertCoreViewOpenRecordInToViewOpenRecordIn'; -import { convertCoreViewSortToViewSort } from '@/views/utils/convertCoreViewSortToViewSort'; import { convertCoreViewTypeToViewType } from '@/views/utils/convertCoreViewTypeToViewType'; export const convertCoreViewToView = ( @@ -37,9 +36,7 @@ export const convertCoreViewToView = ( viewFilterGroups: coreView.viewFilterGroups?.map( convertCoreViewFilterGroupToViewFilterGroup, ), - viewSorts: coreView.viewSorts.map((viewSort) => - convertCoreViewSortToViewSort(viewSort), - ), + viewSorts: coreView.viewSorts, kanbanFieldMetadataId: '', kanbanAggregateOperation: coreView.kanbanAggregateOperation ?? null, kanbanAggregateOperationFieldMetadataId: diff --git a/packages/twenty-front/src/modules/views/utils/convertViewSortDirectionToCore.ts b/packages/twenty-front/src/modules/views/utils/convertViewSortDirectionToCore.ts deleted file mode 100644 index 0eca168c27..0000000000 --- a/packages/twenty-front/src/modules/views/utils/convertViewSortDirectionToCore.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { ViewSortDirection } from '~/generated/graphql'; - -export const convertViewSortDirectionToCore = ( - viewSortDirection: string, -): ViewSortDirection => { - return viewSortDirection === 'asc' - ? ViewSortDirection.ASC - : ViewSortDirection.DESC; -}; diff --git a/packages/twenty-front/src/modules/views/utils/getViewSortsToCreate.ts b/packages/twenty-front/src/modules/views/utils/getViewSortsToCreate.ts index 03d8bd4b34..cf405d0fce 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewSortsToCreate.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewSortsToCreate.ts @@ -1,9 +1,10 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { isDefined } from 'twenty-shared/utils'; +import { type CoreViewSort } from '~/generated/graphql'; export const getViewSortsToCreate = ( - currentViewSorts: ViewSort[], - newViewSorts: ViewSort[], + currentViewSorts: Pick[], + newViewSorts: CoreViewSortEssential[], ) => { return newViewSorts.filter((newViewSort) => { const correspondingViewSort = currentViewSorts.find( diff --git a/packages/twenty-front/src/modules/views/utils/getViewSortsToDelete.ts b/packages/twenty-front/src/modules/views/utils/getViewSortsToDelete.ts index 1949c8d486..b03922c325 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewSortsToDelete.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewSortsToDelete.ts @@ -1,8 +1,8 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; export const getViewSortsToDelete = ( - currentViewSorts: ViewSort[], - newViewSorts: ViewSort[], + currentViewSorts: CoreViewSortEssential[], + newViewSorts: Pick[], ) => { return currentViewSorts.filter( (currentViewSort) => diff --git a/packages/twenty-front/src/modules/views/utils/getViewSortsToUpdate.ts b/packages/twenty-front/src/modules/views/utils/getViewSortsToUpdate.ts index 50ef242297..0c9797f9a6 100644 --- a/packages/twenty-front/src/modules/views/utils/getViewSortsToUpdate.ts +++ b/packages/twenty-front/src/modules/views/utils/getViewSortsToUpdate.ts @@ -1,10 +1,13 @@ -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSortEssential } from '@/views/types/CoreViewSortEssential'; import { areViewSortsEqual } from '@/views/utils/areViewSortsEqual'; import { isDefined } from 'twenty-shared/utils'; export const getViewSortsToUpdate = ( - currentViewSorts: ViewSort[], - newViewSorts: ViewSort[], + currentViewSorts: Pick< + CoreViewSortEssential, + 'fieldMetadataId' | 'direction' + >[], + newViewSorts: CoreViewSortEssential[], ) => { return newViewSorts.filter((newViewSort) => { const correspondingViewSort = currentViewSorts.find( diff --git a/packages/twenty-front/src/modules/views/utils/mapRecordSortToViewSort.ts b/packages/twenty-front/src/modules/views/utils/mapRecordSortToViewSort.ts index 26e3df4274..dc11287873 100644 --- a/packages/twenty-front/src/modules/views/utils/mapRecordSortToViewSort.ts +++ b/packages/twenty-front/src/modules/views/utils/mapRecordSortToViewSort.ts @@ -1,9 +1,12 @@ import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; -import { type ViewSort } from '@/views/types/ViewSort'; +import { type CoreViewSort } from '~/generated/graphql'; -export const mapRecordSortToViewSort = (recordSort: RecordSort): ViewSort => { +export const mapRecordSortToViewSort = ( + recordSort: RecordSort, + viewId: string, +): Pick => { return { - __typename: 'ViewSort', ...recordSort, - } satisfies ViewSort; + viewId, + }; }; diff --git a/packages/twenty-front/src/modules/views/utils/mapViewSortsToSorts.ts b/packages/twenty-front/src/modules/views/utils/mapViewSortsToSorts.ts deleted file mode 100644 index 7545ded676..0000000000 --- a/packages/twenty-front/src/modules/views/utils/mapViewSortsToSorts.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { type RecordSort } from '@/object-record/record-sort/types/RecordSort'; -import { type ViewSort } from '../types/ViewSort'; -import { isDefined } from 'twenty-shared/utils'; - -export const mapViewSortsToSorts = (viewSorts: ViewSort[]): RecordSort[] => { - return viewSorts - .map((viewSort) => { - return { - id: viewSort.id, - fieldMetadataId: viewSort.fieldMetadataId, - direction: viewSort.direction, - }; - }) - .filter(isDefined); -};