Implements new record sort CRUD (#10448)
This PR implements new record sorts CRUD as already done on record filters, which is based on record sorts state instead of combined view sorts. It implements a new useSaveRecordSortsToViewSorts with its underlying utils, to compute diff between two view sorts array. The associated unit tests have also been written. This PR also fixes the bug where the view bar disappeared when deleting the already saved record sort of a view.
This commit is contained in:
@ -179,8 +179,8 @@ export const ViewBarDetails = ({
|
||||
|
||||
const shouldExpandViewBar =
|
||||
viewFiltersAreDifferentFromRecordFilters ||
|
||||
((currentViewWithCombinedFiltersAndSorts?.viewSorts?.length ||
|
||||
currentRecordFilters?.length) &&
|
||||
viewSortsAreDifferentFromRecordSorts ||
|
||||
((currentRecordSorts?.length || currentRecordFilters?.length) &&
|
||||
isViewBarExpanded);
|
||||
|
||||
if (!shouldExpandViewBar) {
|
||||
|
||||
@ -1,14 +1,46 @@
|
||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { areViewSortsDifferentFromRecordSortsSelector } from '@/views/states/selectors/areViewSortsDifferentFromRecordSortsFamilySelector';
|
||||
import { getViewSortsToCreate } from '@/views/utils/getViewSortsToCreate';
|
||||
import { getViewSortsToDelete } from '@/views/utils/getViewSortsToDelete';
|
||||
import { getViewSortsToUpdate } from '@/views/utils/getViewSortsToUpdate';
|
||||
import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useAreViewSortsDifferentFromRecordSorts = () => {
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const viewSortsAreDifferentFromRecordSorts = useRecoilComponentFamilyValueV2(
|
||||
areViewSortsDifferentFromRecordSortsSelector,
|
||||
{ viewId: currentView?.id },
|
||||
const currentRecordSorts = useRecoilComponentValueV2(
|
||||
currentRecordSortsComponentState,
|
||||
);
|
||||
|
||||
const viewSortsAreDifferentFromRecordSorts = useMemo(() => {
|
||||
const currentViewSorts = currentView?.viewSorts ?? [];
|
||||
const viewSortsFromCurrentRecordSorts = currentRecordSorts.map(
|
||||
mapRecordSortToViewSort,
|
||||
);
|
||||
|
||||
const viewSortsToCreate = getViewSortsToCreate(
|
||||
currentViewSorts,
|
||||
viewSortsFromCurrentRecordSorts,
|
||||
);
|
||||
|
||||
const viewSortsToDelete = getViewSortsToDelete(
|
||||
currentViewSorts,
|
||||
viewSortsFromCurrentRecordSorts,
|
||||
);
|
||||
|
||||
const viewSortsToUpdate = getViewSortsToUpdate(
|
||||
currentViewSorts,
|
||||
viewSortsFromCurrentRecordSorts,
|
||||
);
|
||||
|
||||
const sortsHaveChanged =
|
||||
viewSortsToCreate.length > 0 ||
|
||||
viewSortsToDelete.length > 0 ||
|
||||
viewSortsToUpdate.length > 0;
|
||||
|
||||
return sortsHaveChanged;
|
||||
}, [currentRecordSorts, currentView]);
|
||||
|
||||
return { viewSortsAreDifferentFromRecordSorts };
|
||||
};
|
||||
|
||||
@ -4,14 +4,12 @@ import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { usePersistViewFilterGroupRecords } from '@/views/hooks/internal/usePersistViewFilterGroupRecords';
|
||||
import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSortRecords';
|
||||
import { useGetViewFromPrefetchState } from '@/views/hooks/useGetViewFromPrefetchState';
|
||||
import { useResetUnsavedViewStates } from '@/views/hooks/useResetUnsavedViewStates';
|
||||
import { useSaveRecordFiltersToViewFilters } from '@/views/hooks/useSaveRecordFiltersToViewFilters';
|
||||
import { useSaveRecordSortsToViewSorts } from '@/views/hooks/useSaveRecordSortsToViewSorts';
|
||||
import { unsavedToDeleteViewFilterGroupIdsComponentFamilyState } from '@/views/states/unsavedToDeleteViewFilterGroupIdsComponentFamilyState';
|
||||
import { unsavedToDeleteViewSortIdsComponentFamilyState } from '@/views/states/unsavedToDeleteViewSortIdsComponentFamilyState';
|
||||
import { unsavedToUpsertViewFilterGroupsComponentFamilyState } from '@/views/states/unsavedToUpsertViewFilterGroupsComponentFamilyState';
|
||||
import { unsavedToUpsertViewSortsComponentFamilyState } from '@/views/states/unsavedToUpsertViewSortsComponentFamilyState';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
|
||||
|
||||
@ -25,18 +23,6 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
viewBarComponentId,
|
||||
);
|
||||
|
||||
const unsavedToDeleteViewSortIdsCallbackState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
unsavedToDeleteViewSortIdsComponentFamilyState,
|
||||
viewBarComponentId,
|
||||
);
|
||||
|
||||
const unsavedToUpsertViewSortsCallbackState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
unsavedToUpsertViewSortsComponentFamilyState,
|
||||
viewBarComponentId,
|
||||
);
|
||||
|
||||
const unsavedToUpsertViewFilterGroupsCallbackState =
|
||||
useRecoilComponentCallbackStateV2(
|
||||
unsavedToUpsertViewFilterGroupsComponentFamilyState,
|
||||
@ -49,12 +35,6 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
viewBarComponentId,
|
||||
);
|
||||
|
||||
const {
|
||||
createViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
} = usePersistViewSortRecords();
|
||||
|
||||
const {
|
||||
createViewFilterGroupRecords,
|
||||
deleteViewFilterGroupRecords,
|
||||
@ -64,52 +44,6 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
const { resetUnsavedViewStates } =
|
||||
useResetUnsavedViewStates(viewBarComponentId);
|
||||
|
||||
const saveViewSorts = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
const unsavedToDeleteViewSortIds = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToDeleteViewSortIdsCallbackState({ viewId }),
|
||||
);
|
||||
|
||||
const unsavedToUpsertViewSorts = getSnapshotValue(
|
||||
snapshot,
|
||||
unsavedToUpsertViewSortsCallbackState({ viewId }),
|
||||
);
|
||||
|
||||
const view = await getViewFromPrefetchState(viewId);
|
||||
|
||||
if (isUndefinedOrNull(view)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewSortsToCreate = unsavedToUpsertViewSorts.filter(
|
||||
(viewSort) =>
|
||||
!view.viewSorts.some(
|
||||
(vs) => vs.fieldMetadataId === viewSort.fieldMetadataId,
|
||||
),
|
||||
);
|
||||
|
||||
const viewSortsToUpdate = unsavedToUpsertViewSorts.filter((viewSort) =>
|
||||
view.viewSorts.some(
|
||||
(vs) => vs.fieldMetadataId === viewSort.fieldMetadataId,
|
||||
),
|
||||
);
|
||||
|
||||
await createViewSortRecords(viewSortsToCreate, view);
|
||||
await updateViewSortRecords(viewSortsToUpdate);
|
||||
await deleteViewSortRecords(unsavedToDeleteViewSortIds);
|
||||
},
|
||||
[
|
||||
createViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
getViewFromPrefetchState,
|
||||
unsavedToDeleteViewSortIdsCallbackState,
|
||||
unsavedToUpsertViewSortsCallbackState,
|
||||
updateViewSortRecords,
|
||||
],
|
||||
);
|
||||
|
||||
const saveViewFilterGroups = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewId: string) => {
|
||||
@ -162,6 +96,8 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
const { saveRecordFiltersToViewFilters } =
|
||||
useSaveRecordFiltersToViewFilters();
|
||||
|
||||
const { saveRecordSortsToViewSorts } = useSaveRecordSortsToViewSorts();
|
||||
|
||||
const saveCurrentViewFilterAndSorts = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async (viewIdFromProps?: string) => {
|
||||
@ -176,8 +112,8 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
const viewId = viewIdFromProps ?? currentViewId;
|
||||
|
||||
await saveViewFilterGroups(viewId);
|
||||
await saveViewSorts(viewId);
|
||||
|
||||
await saveRecordSortsToViewSorts();
|
||||
await saveRecordFiltersToViewFilters();
|
||||
|
||||
resetUnsavedViewStates(viewId);
|
||||
@ -185,9 +121,9 @@ export const useSaveCurrentViewFiltersAndSorts = (
|
||||
[
|
||||
currentViewIdCallbackState,
|
||||
resetUnsavedViewStates,
|
||||
saveViewSorts,
|
||||
saveViewFilterGroups,
|
||||
saveRecordFiltersToViewFilters,
|
||||
saveRecordSortsToViewSorts,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
import { currentRecordSortsComponentState } from '@/object-record/record-sort/states/currentRecordSortsComponentState';
|
||||
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { usePersistViewSortRecords } from '@/views/hooks/internal/usePersistViewSortRecords';
|
||||
import { useGetCurrentViewOnly } from '@/views/hooks/useGetCurrentViewOnly';
|
||||
import { getViewSortsToCreate } from '@/views/utils/getViewSortsToCreate';
|
||||
import { getViewSortsToDelete } from '@/views/utils/getViewSortsToDelete';
|
||||
import { getViewSortsToUpdate } from '@/views/utils/getViewSortsToUpdate';
|
||||
import { mapRecordSortToViewSort } from '@/views/utils/mapRecordSortToViewSort';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const useSaveRecordSortsToViewSorts = () => {
|
||||
const {
|
||||
createViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
} = usePersistViewSortRecords();
|
||||
|
||||
const { currentView } = useGetCurrentViewOnly();
|
||||
|
||||
const currentRecordSortsCallbackState = useRecoilComponentCallbackStateV2(
|
||||
currentRecordSortsComponentState,
|
||||
);
|
||||
|
||||
const saveRecordSortsToViewSorts = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
async () => {
|
||||
if (!isDefined(currentView)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentViewSorts = currentView?.viewSorts ?? [];
|
||||
|
||||
const currentRecordSorts = getSnapshotValue(
|
||||
snapshot,
|
||||
currentRecordSortsCallbackState,
|
||||
);
|
||||
|
||||
const newViewSorts = currentRecordSorts.map(mapRecordSortToViewSort);
|
||||
|
||||
const viewSortsToCreate = getViewSortsToCreate(
|
||||
currentViewSorts,
|
||||
newViewSorts,
|
||||
);
|
||||
|
||||
const viewSortsToDelete = getViewSortsToDelete(
|
||||
currentViewSorts,
|
||||
newViewSorts,
|
||||
);
|
||||
|
||||
const viewSortsToUpdate = getViewSortsToUpdate(
|
||||
currentViewSorts,
|
||||
newViewSorts,
|
||||
);
|
||||
|
||||
const viewSortIdsToDelete = viewSortsToDelete.map(
|
||||
(viewSort) => viewSort.id,
|
||||
);
|
||||
|
||||
await createViewSortRecords(viewSortsToCreate, currentView);
|
||||
await updateViewSortRecords(viewSortsToUpdate);
|
||||
await deleteViewSortRecords(viewSortIdsToDelete);
|
||||
},
|
||||
[
|
||||
createViewSortRecords,
|
||||
deleteViewSortRecords,
|
||||
updateViewSortRecords,
|
||||
currentRecordSortsCallbackState,
|
||||
currentView,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
saveRecordSortsToViewSorts,
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,40 @@
|
||||
import { RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { areViewSortsEqual } from '@/views/utils/areViewSortsEqual';
|
||||
|
||||
describe('areViewSortsEqual', () => {
|
||||
const baseSort: ViewSort = {
|
||||
__typename: 'ViewSort',
|
||||
id: 'sort-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
direction: 'asc',
|
||||
};
|
||||
|
||||
it('should return true when all comparable properties are equal', () => {
|
||||
const sortA = { ...baseSort };
|
||||
const sortB = { ...baseSort };
|
||||
|
||||
expect(areViewSortsEqual(sortA, sortB)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when displayValue is different', () => {
|
||||
const sortA = { ...baseSort };
|
||||
const sortB = { ...baseSort, direction: 'desc' as RecordSortDirection };
|
||||
|
||||
expect(areViewSortsEqual(sortA, sortB)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when fieldMetadataId is different', () => {
|
||||
const sortA = { ...baseSort };
|
||||
const sortB = { ...baseSort, fieldMetadataId: 'field-2' };
|
||||
|
||||
expect(areViewSortsEqual(sortA, sortB)).toBe(false);
|
||||
});
|
||||
|
||||
it('should ignore non-comparable properties', () => {
|
||||
const sortA = { ...baseSort, id: 'id-1', createdAt: '2023-01-01' };
|
||||
const sortB = { ...baseSort, id: 'id-2', createdAt: '2023-01-02' };
|
||||
|
||||
expect(areViewSortsEqual(sortA, sortB)).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,84 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { getViewSortsToCreate } from '../getViewSortsToCreate';
|
||||
|
||||
describe('getViewSortsToCreate', () => {
|
||||
const baseSort: ViewSort = {
|
||||
__typename: 'ViewSort',
|
||||
id: 'sort-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
direction: 'asc',
|
||||
};
|
||||
|
||||
it('should return all sorts when current sorts array is empty', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [
|
||||
{ ...baseSort },
|
||||
{
|
||||
...baseSort,
|
||||
id: 'sort-2',
|
||||
fieldMetadataId: 'field-2',
|
||||
} satisfies ViewSort,
|
||||
];
|
||||
|
||||
const result = getViewSortsToCreate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual(newViewSorts);
|
||||
});
|
||||
|
||||
it('should return empty array when new sorts array is empty', () => {
|
||||
const currentViewSorts: ViewSort[] = [baseSort];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToCreate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return only sorts that do not exist in current sorts', () => {
|
||||
const existingSort = { ...baseSort };
|
||||
const newSortWithDifferentFieldMetadataId = {
|
||||
...baseSort,
|
||||
id: 'sort-2',
|
||||
fieldMetadataId: 'field-2',
|
||||
} satisfies ViewSort;
|
||||
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
|
||||
const newViewSorts: ViewSort[] = [
|
||||
existingSort,
|
||||
newSortWithDifferentFieldMetadataId,
|
||||
];
|
||||
|
||||
const result = getViewSortsToCreate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([newSortWithDifferentFieldMetadataId]);
|
||||
});
|
||||
|
||||
it('should handle sorts with different fieldMetadataIds', () => {
|
||||
const existingSort = { ...baseSort };
|
||||
const sortWithDifferentFieldMetadataId = {
|
||||
...baseSort,
|
||||
fieldMetadataId: 'group-2',
|
||||
} satisfies ViewSort;
|
||||
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
|
||||
const newViewSorts: ViewSort[] = [
|
||||
existingSort,
|
||||
sortWithDifferentFieldMetadataId,
|
||||
];
|
||||
|
||||
const result = getViewSortsToCreate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([sortWithDifferentFieldMetadataId]);
|
||||
});
|
||||
|
||||
it('should handle empty arrays for both inputs', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToCreate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,72 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { getViewSortsToDelete } from '../getViewSortsToDelete';
|
||||
|
||||
describe('getViewSortsToDelete', () => {
|
||||
const baseSort: ViewSort = {
|
||||
__typename: 'ViewSort',
|
||||
id: 'sort-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
direction: 'asc',
|
||||
};
|
||||
|
||||
it('should return empty array when current sorts array is empty', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [baseSort];
|
||||
|
||||
const result = getViewSortsToDelete(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return all current sorts when new sorts array is empty', () => {
|
||||
const existingSort = { ...baseSort };
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToDelete(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([existingSort]);
|
||||
});
|
||||
|
||||
it('should return sorts that exist in current but not in new sorts', () => {
|
||||
const sortToDelete = { ...baseSort };
|
||||
const sortToKeep = {
|
||||
...baseSort,
|
||||
id: 'filter-2',
|
||||
fieldMetadataId: 'field-2',
|
||||
} satisfies ViewSort;
|
||||
|
||||
const currentViewSorts: ViewSort[] = [sortToDelete, sortToKeep];
|
||||
const newViewSorts: ViewSort[] = [sortToKeep];
|
||||
|
||||
const result = getViewSortsToDelete(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([sortToDelete]);
|
||||
});
|
||||
|
||||
it('should handle empty arrays for both inputs', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToDelete(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
const newViewSorts: ViewSort[] = [matchingSort];
|
||||
|
||||
const result = getViewSortsToDelete(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,66 @@
|
||||
import { RecordSortDirection } from '@/object-record/record-sort/types/RecordSortDirection';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { getViewSortsToUpdate } from '../getViewSortsToUpdate';
|
||||
|
||||
describe('getViewSortsToUpdate', () => {
|
||||
const baseSort: ViewSort = {
|
||||
__typename: 'ViewSort',
|
||||
id: 'sort-1',
|
||||
fieldMetadataId: 'field-1',
|
||||
direction: 'asc' as RecordSortDirection,
|
||||
};
|
||||
|
||||
it('should return empty array when current sorts array is empty', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [baseSort];
|
||||
|
||||
const result = getViewSortsToUpdate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty array when new sorts array is empty', () => {
|
||||
const currentViewSorts: ViewSort[] = [baseSort];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToUpdate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return sorts that exist in both arrays but have different direction', () => {
|
||||
const existingSort = { ...baseSort };
|
||||
const updatedSort = {
|
||||
...baseSort,
|
||||
direction: 'desc',
|
||||
} satisfies ViewSort;
|
||||
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
const newViewSorts: ViewSort[] = [updatedSort];
|
||||
|
||||
const result = getViewSortsToUpdate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([updatedSort]);
|
||||
});
|
||||
|
||||
it('should not return sorts that exist in both arrays with same values', () => {
|
||||
const existingSort = { ...baseSort };
|
||||
const sameSort = { ...baseSort };
|
||||
|
||||
const currentViewSorts: ViewSort[] = [existingSort];
|
||||
const newViewSorts: ViewSort[] = [sameSort];
|
||||
|
||||
const result = getViewSortsToUpdate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should handle empty arrays for both inputs', () => {
|
||||
const currentViewSorts: ViewSort[] = [];
|
||||
const newViewSorts: ViewSort[] = [];
|
||||
|
||||
const result = getViewSortsToUpdate(currentViewSorts, newViewSorts);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,12 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const areViewSortsEqual = (viewSortA: ViewSort, viewSortB: ViewSort) => {
|
||||
const propertiesToCompare: (keyof ViewSort)[] = [
|
||||
'fieldMetadataId',
|
||||
'direction',
|
||||
];
|
||||
|
||||
return propertiesToCompare.every(
|
||||
(property) => viewSortA[property] === viewSortB[property],
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,18 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const getViewSortsToCreate = (
|
||||
currentViewSorts: ViewSort[],
|
||||
newViewSorts: ViewSort[],
|
||||
) => {
|
||||
return newViewSorts.filter((newViewSort) => {
|
||||
const correspondingViewSort = currentViewSorts.find(
|
||||
(currentViewSort) =>
|
||||
currentViewSort.fieldMetadataId === newViewSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
const shouldCreateBecauseViewSortIsNew = !isDefined(correspondingViewSort);
|
||||
|
||||
return shouldCreateBecauseViewSortIsNew;
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const getViewSortsToDelete = (
|
||||
currentViewSorts: ViewSort[],
|
||||
newViewSorts: ViewSort[],
|
||||
) => {
|
||||
return currentViewSorts.filter(
|
||||
(currentViewSort) =>
|
||||
!newViewSorts.some(
|
||||
(newViewSort) =>
|
||||
newViewSort.fieldMetadataId === currentViewSort.fieldMetadataId,
|
||||
),
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,26 @@
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
import { areViewSortsEqual } from '@/views/utils/areViewSortsEqual';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
export const getViewSortsToUpdate = (
|
||||
currentViewSorts: ViewSort[],
|
||||
newViewSorts: ViewSort[],
|
||||
) => {
|
||||
return newViewSorts.filter((newViewSort) => {
|
||||
const correspondingViewSort = currentViewSorts.find(
|
||||
(currentViewSort) =>
|
||||
currentViewSort.fieldMetadataId === newViewSort.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!isDefined(correspondingViewSort)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const shouldUpdateBecauseViewSortIsDifferent = !areViewSortsEqual(
|
||||
newViewSort,
|
||||
correspondingViewSort,
|
||||
);
|
||||
|
||||
return shouldUpdateBecauseViewSortIsDifferent;
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,9 @@
|
||||
import { RecordSort } from '@/object-record/record-sort/types/RecordSort';
|
||||
import { ViewSort } from '@/views/types/ViewSort';
|
||||
|
||||
export const mapRecordSortToViewSort = (recordSort: RecordSort): ViewSort => {
|
||||
return {
|
||||
__typename: 'ViewSort',
|
||||
...recordSort,
|
||||
} satisfies ViewSort;
|
||||
};
|
||||
Reference in New Issue
Block a user