diff --git a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx index 9a4aa4af6..6c0b8cf3d 100644 --- a/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx +++ b/packages/twenty-front/src/pages/settings/data-model/SettingsObjectDetail.tsx @@ -25,6 +25,7 @@ import { Table } from '@/ui/layout/table/components/Table'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; import { TableSection } from '@/ui/layout/table/components/TableSection'; import { Breadcrumb } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; +import { sortFieldMetadataItem } from '~/utils/sortFieldMetadataItem'; const StyledDiv = styled.div` display: flex; @@ -51,12 +52,16 @@ export const SettingsObjectDetail = () => { if (!activeObjectMetadataItem) return null; - const activeMetadataFields = activeObjectMetadataItem.fields.filter( - (metadataField) => metadataField.isActive && !metadataField.isSystem, - ); - const disabledMetadataFields = activeObjectMetadataItem.fields.filter( - (metadataField) => !metadataField.isActive && !metadataField.isSystem, - ); + const activeMetadataFields = activeObjectMetadataItem.fields + .filter( + (metadataField) => metadataField.isActive && !metadataField.isSystem, + ) + .sort(sortFieldMetadataItem); + const disabledMetadataFields = activeObjectMetadataItem.fields + .filter( + (metadataField) => !metadataField.isActive && !metadataField.isSystem, + ) + .sort(sortFieldMetadataItem); const handleDisableObject = async () => { await disableObjectMetadataItem(activeObjectMetadataItem); diff --git a/packages/twenty-front/src/utils/__tests__/sortFieldMetadataItem.test.ts b/packages/twenty-front/src/utils/__tests__/sortFieldMetadataItem.test.ts new file mode 100644 index 000000000..56256c13e --- /dev/null +++ b/packages/twenty-front/src/utils/__tests__/sortFieldMetadataItem.test.ts @@ -0,0 +1,96 @@ +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; + +import { sortFieldMetadataItem } from '../sortFieldMetadataItem'; + +describe('sortFieldMetadataItem', () => { + it('should return an empty array for an empty array', () => { + const items: FieldMetadataItem[] = []; + const sortedItems = items.sort(sortFieldMetadataItem); + const expectedItems: FieldMetadataItem[] = []; + expect(sortedItems).toEqual(expectedItems); + }); + + it('should return array with a single item for the same array', () => { + const item1 = { + isCustom: false, + createdAt: '2024-01-03T11:41:16.344Z', + } as FieldMetadataItem; + + const items: FieldMetadataItem[] = [item1]; + const sortedItems = items.sort(sortFieldMetadataItem); + const expectedItems: FieldMetadataItem[] = [item1]; + expect(sortedItems).toEqual(expectedItems); + }); + + it('should correctly sort items based on createdAt', () => { + const item1 = { + isCustom: false, + createdAt: '2024-01-03T11:41:16.344Z', + } as FieldMetadataItem; + + const item2 = { + isCustom: false, + createdAt: '2024-01-02T09:41:16.344Z', + } as FieldMetadataItem; + + const item3 = { + isCustom: false, + createdAt: '2024-01-03T09:41:16.344Z', + } as FieldMetadataItem; + + const items = [item1, item2, item3]; + const sortedItems = items.sort(sortFieldMetadataItem); + const expectedItems = [item2, item3, item1]; + + expect(sortedItems).toEqual(expectedItems); + }); + + it('should correctly sort items based on isCustom and createdAt', () => { + const item1 = { + isCustom: false, + createdAt: '2024-01-03T09:41:16.344Z', + } as FieldMetadataItem; + + const item2 = { + isCustom: true, + createdAt: '2024-01-02T09:41:16.344Z', + } as FieldMetadataItem; + + const item3 = { + isCustom: false, + createdAt: '2024-01-01T09:41:16.344Z', + } as FieldMetadataItem; + + const item4 = { + isCustom: true, + createdAt: '2024-01-04T09:41:16.344Z', + } as FieldMetadataItem; + + const items = [item1, item2, item3, item4]; + const sortedItems = items.sort(sortFieldMetadataItem); + const expectedItems = [item3, item1, item2, item4]; + + expect(sortedItems).toEqual(expectedItems); + }); + + it('should handle arrays with items missing required properties', () => { + const item1 = { + isCustom: false, + createdAt: '2024-01-03T11:41:16.344Z', + } as FieldMetadataItem; + + const item2 = { + // Missing createdAt property + isCustom: false, + } as FieldMetadataItem; + + const item3 = { + // Missing isCustom property + createdAt: '2024-01-05T11:41:16.344Z', + } as FieldMetadataItem; + + const items = [item1, item2, item3]; + const sortedItems = items.sort(sortFieldMetadataItem); + expect(sortedItems).toHaveLength(items.length); + }); +}); diff --git a/packages/twenty-front/src/utils/sortFieldMetadataItem.ts b/packages/twenty-front/src/utils/sortFieldMetadataItem.ts new file mode 100644 index 000000000..ddac258cf --- /dev/null +++ b/packages/twenty-front/src/utils/sortFieldMetadataItem.ts @@ -0,0 +1,20 @@ +import { FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem'; + +import { parseDate } from '../utils/date-utils'; + +export const sortFieldMetadataItem = ( + a: FieldMetadataItem, + b: FieldMetadataItem, +) => { + const customCompare = a.isCustom === b.isCustom ? 0 : a.isCustom ? 1 : -1; + if (customCompare !== 0) return customCompare; + + const dateA = a.createdAt ? parseDate(a.createdAt) : null; + const dateB = b.createdAt ? parseDate(b.createdAt) : null; + + if (!dateA && !dateB) return 0; + if (!dateA) return 1; + if (!dateB) return -1; + + return dateB.diff(dateA).milliseconds > 0 ? -1 : 1; +};