diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/components/EventList.tsx b/packages/twenty-front/src/modules/activities/timelineActivities/components/EventList.tsx index 803f36160..49692cee1 100644 --- a/packages/twenty-front/src/modules/activities/timelineActivities/components/EventList.tsx +++ b/packages/twenty-front/src/modules/activities/timelineActivities/components/EventList.tsx @@ -1,8 +1,9 @@ -import { ReactElement } from 'react'; import styled from '@emotion/styled'; +import { ReactElement } from 'react'; import { EventsGroup } from '@/activities/timelineActivities/components/EventsGroup'; import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity'; +import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities'; import { groupEventsByMonth } from '@/activities/timelineActivities/utils/groupEventsByMonth'; import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; @@ -29,12 +30,17 @@ const StyledTimelineContainer = styled.div` `; export const EventList = ({ events, targetableObject }: EventListProps) => { - const groupedEvents = groupEventsByMonth(events); - const mainObjectMetadataItem = useObjectMetadataItem({ objectNameSingular: targetableObject.targetObjectNameSingular, }).objectMetadataItem; + const filteredEvents = filterOutInvalidTimelineActivities( + events, + mainObjectMetadataItem, + ); + + const groupedEvents = groupEventsByMonth(filteredEvents); + return ( diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts b/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts new file mode 100644 index 000000000..74ecda0c1 --- /dev/null +++ b/packages/twenty-front/src/modules/activities/timelineActivities/utils/__tests__/filterOutInvalidTimelineActivities.test.ts @@ -0,0 +1,117 @@ +import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity'; +import { filterOutInvalidTimelineActivities } from '@/activities/timelineActivities/utils/filterOutInvalidTimelineActivities'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; + +describe('filterOutInvalidTimelineActivities', () => { + it('should filter out TimelineActivities with deleted fields from the properties diff', () => { + const events = [ + { + id: '1', + properties: { + diff: { + field1: { before: 'value1', after: 'value2' }, + field2: { before: 'value3', after: 'value4' }, + field3: { before: 'value5', after: 'value6' }, + }, + }, + }, + { + id: '2', + properties: { + diff: { + field1: { before: 'value7', after: 'value8' }, + field2: { before: 'value9', after: 'value10' }, + field4: { before: 'value11', after: 'value12' }, + }, + }, + }, + ] as TimelineActivity[]; + + const mainObjectMetadataItem = { + fields: [{ name: 'field1' }, { name: 'field2' }, { name: 'field3' }], + } as ObjectMetadataItem; + + const filteredEvents = filterOutInvalidTimelineActivities( + events, + mainObjectMetadataItem, + ); + + expect(filteredEvents).toEqual([ + { + id: '1', + properties: { + diff: { + field1: { before: 'value1', after: 'value2' }, + field2: { before: 'value3', after: 'value4' }, + field3: { before: 'value5', after: 'value6' }, + }, + }, + }, + { + id: '2', + properties: { + diff: { + field1: { before: 'value7', after: 'value8' }, + field2: { before: 'value9', after: 'value10' }, + }, + }, + }, + ]); + }); + + it('should return an empty array if all TimelineActivities have deleted fields in the properties diff', () => { + const events = [ + { + id: '1', + properties: { + diff: { + field3: { before: 'value5', after: 'value6' }, + }, + }, + }, + { + id: '2', + properties: { + diff: { + field4: { before: 'value11', after: 'value12' }, + }, + }, + }, + ] as TimelineActivity[]; + + const mainObjectMetadataItem = { + fields: [{ name: 'field1' }, { name: 'field2' }], + } as ObjectMetadataItem; + + const filteredEvents = filterOutInvalidTimelineActivities( + events, + mainObjectMetadataItem, + ); + + expect(filteredEvents).toEqual([]); + }); + + it('should return the same TimelineActivities if there are no properties diffs', () => { + const events = [ + { + id: '1', + properties: {}, + }, + { + id: '2', + properties: {}, + }, + ] as TimelineActivity[]; + + const mainObjectMetadataItem = { + fields: [{ name: 'field1' }, { name: 'field2' }], + } as ObjectMetadataItem; + + const filteredEvents = filterOutInvalidTimelineActivities( + events, + mainObjectMetadataItem, + ); + + expect(filteredEvents).toEqual(events); + }); +}); diff --git a/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts b/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts new file mode 100644 index 000000000..a7badf00b --- /dev/null +++ b/packages/twenty-front/src/modules/activities/timelineActivities/utils/filterOutInvalidTimelineActivities.ts @@ -0,0 +1,35 @@ +import { TimelineActivity } from '@/activities/timelineActivities/types/TimelineActivity'; +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; + +export const filterOutInvalidTimelineActivities = ( + timelineActivities: TimelineActivity[], + mainObjectMetadataItem: ObjectMetadataItem, +): TimelineActivity[] => { + const fieldMetadataItemMap = new Map( + mainObjectMetadataItem.fields.map((field) => [field.name, field]), + ); + + return timelineActivities.filter((timelineActivity) => { + const diff = timelineActivity.properties?.diff; + const canSkipValidation = !diff; + + if (canSkipValidation) { + return true; + } + + const validDiffEntries = Object.entries(diff).filter(([diffKey]) => + fieldMetadataItemMap.has(diffKey), + ); + + if (validDiffEntries.length === 0) { + return false; + } + + timelineActivity.properties = { + ...timelineActivity.properties, + diff: Object.fromEntries(validDiffEntries), + }; + + return true; + }); +};