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
This commit is contained in:
@@ -62,7 +62,7 @@ const jestConfig = {
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 53,
|
||||
lines: 53,
|
||||
lines: 52,
|
||||
functions: 42,
|
||||
},
|
||||
},
|
||||
|
||||
+13
-13
@@ -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 = () => {
|
||||
</DropdownMenuHeader>
|
||||
<DropdownMenuInnerSelect
|
||||
dropdownId="record-sort-direction-dropdown"
|
||||
options={RECORD_SORT_DIRECTIONS.map((sortDirection) => ({
|
||||
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}
|
||||
/>
|
||||
|
||||
+2
-1
@@ -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 {
|
||||
|
||||
+3
-3
@@ -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<RecordSortDirection>({
|
||||
createComponentState<ViewSortDirection>({
|
||||
key: 'selectedRecordSortDirectionComponentState',
|
||||
defaultValue: 'asc',
|
||||
defaultValue: ViewSortDirection.ASC,
|
||||
componentInstanceContext: ObjectSortDropdownComponentInstanceContext,
|
||||
});
|
||||
|
||||
+9
-6
@@ -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: [],
|
||||
|
||||
+4
-1
@@ -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);
|
||||
})
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
export const RECORD_SORT_DIRECTIONS = ['asc', 'desc'] as const;
|
||||
|
||||
export type RecordSortDirection = (typeof RECORD_SORT_DIRECTIONS)[number];
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+8
-13
@@ -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<CoreViewSort, 'workspaceId'> = {
|
||||
__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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
+6
-6
@@ -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,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
+32
-76
@@ -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<GraphQLView, 'id'>) => {
|
||||
(
|
||||
viewSortsToCreate: CoreViewSortEssential[],
|
||||
view: Pick<GraphQLView, 'id'>,
|
||||
) => {
|
||||
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<CoreViewSort>,
|
||||
},
|
||||
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<CoreViewSort>,
|
||||
},
|
||||
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<ViewSort>(
|
||||
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<CoreViewSortEssential, 'id' | 'viewId'>[]) => {
|
||||
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<ViewSort>(
|
||||
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 {
|
||||
|
||||
+5
-2
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+9
-6
@@ -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 {
|
||||
|
||||
+5
-1
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
+1
-2
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
+141
@@ -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<CoreViewSort, 'workspaceId'>[];
|
||||
updatedViewSorts?: Omit<CoreViewSort, 'workspaceId'>[];
|
||||
deletedViewSorts?: Pick<CoreViewSort, 'id' | 'viewId'>[];
|
||||
}) => {
|
||||
const coreViews = getSnapshotValue(snapshot, coreViewsState);
|
||||
let newCoreViews = [...coreViews];
|
||||
|
||||
createdViewSorts.forEach((createdViewSort) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
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<CoreViewWithRelations>({
|
||||
id: cache.identify({
|
||||
__typename: 'CoreView',
|
||||
id: updatedViewSort.viewId,
|
||||
}),
|
||||
fields: {
|
||||
viewSorts: (existingViewSorts, { readField, toReference }) =>
|
||||
existingViewSorts.map((viewSort) => {
|
||||
const viewSortId = readField<string>('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<CoreViewSort, 'id' | 'viewId'>) => {
|
||||
cache.modify<CoreViewWithRelations>({
|
||||
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,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,6 @@
|
||||
import { type CoreViewSort } from '~/generated/graphql';
|
||||
|
||||
export type CoreViewSortEssential = Pick<
|
||||
CoreViewSort,
|
||||
'id' | 'fieldMetadataId' | 'direction' | 'viewId'
|
||||
>;
|
||||
@@ -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<CoreViewSort, 'workspaceId' | 'createdAt' | 'updatedAt'>[];
|
||||
viewSorts: CoreViewSortEssential[];
|
||||
kanbanAggregateOperation?: AggregateOperations | null;
|
||||
kanbanAggregateOperationFieldMetadataId?: string | null;
|
||||
position: number;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
+15
-14
@@ -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);
|
||||
|
||||
|
||||
+23
-19
@@ -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);
|
||||
|
||||
|
||||
+17
-17
@@ -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);
|
||||
|
||||
|
||||
@@ -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[] = [
|
||||
|
||||
@@ -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<CoreViewSort, 'fieldMetadataId' | 'direction'>,
|
||||
viewSortB: Pick<CoreViewSort, 'fieldMetadataId' | 'direction'>,
|
||||
) => {
|
||||
const propertiesToCompare: (keyof Pick<
|
||||
CoreViewSort,
|
||||
'fieldMetadataId' | 'direction'
|
||||
>)[] = ['fieldMetadataId', 'direction'];
|
||||
|
||||
return propertiesToCompare.every((property) =>
|
||||
compareStrictlyExceptForNullAndUndefined(
|
||||
viewSortA[property],
|
||||
viewSortB[property],
|
||||
viewSortA[
|
||||
property as keyof Pick<CoreViewSort, 'fieldMetadataId' | 'direction'>
|
||||
],
|
||||
viewSortB[
|
||||
property as keyof Pick<CoreViewSort, 'fieldMetadataId' | 'direction'>
|
||||
],
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
import { type ViewSort } from '@/views/types/ViewSort';
|
||||
import { type CoreViewSort, ViewSortDirection } from '~/generated/graphql';
|
||||
|
||||
export const convertCoreViewSortToViewSort = (
|
||||
coreViewSort: Pick<CoreViewSort, 'id' | 'fieldMetadataId' | 'direction'>,
|
||||
): ViewSort => {
|
||||
return {
|
||||
__typename: 'ViewSort',
|
||||
id: coreViewSort.id,
|
||||
fieldMetadataId: coreViewSort.fieldMetadataId,
|
||||
direction:
|
||||
coreViewSort.direction === ViewSortDirection.ASC ? 'asc' : 'desc',
|
||||
};
|
||||
};
|
||||
@@ -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:
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { ViewSortDirection } from '~/generated/graphql';
|
||||
|
||||
export const convertViewSortDirectionToCore = (
|
||||
viewSortDirection: string,
|
||||
): ViewSortDirection => {
|
||||
return viewSortDirection === 'asc'
|
||||
? ViewSortDirection.ASC
|
||||
: ViewSortDirection.DESC;
|
||||
};
|
||||
@@ -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<CoreViewSort, 'fieldMetadataId'>[],
|
||||
newViewSorts: CoreViewSortEssential[],
|
||||
) => {
|
||||
return newViewSorts.filter((newViewSort) => {
|
||||
const correspondingViewSort = currentViewSorts.find(
|
||||
|
||||
@@ -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<CoreViewSortEssential, 'fieldMetadataId'>[],
|
||||
) => {
|
||||
return currentViewSorts.filter(
|
||||
(currentViewSort) =>
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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<CoreViewSort, 'id' | 'fieldMetadataId' | 'direction' | 'viewId'> => {
|
||||
return {
|
||||
__typename: 'ViewSort',
|
||||
...recordSort,
|
||||
} satisfies ViewSort;
|
||||
viewId,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
Reference in New Issue
Block a user