From efab98a8f8b120ab49b01bba23500e498625c8b7 Mon Sep 17 00:00:00 2001 From: Baptiste Devessier Date: Tue, 22 Apr 2025 16:45:36 +0200 Subject: [PATCH] Get all first depth fields in the table and the board (#11650) After discussing it with the team, we now want to query all fields in the table and the board by default. Feeding the cache with exhaustive data will make the side panel's life easier, as it needs all the record fields to determine the actions to enable. --- .../tests/workflow-run.spec.ts | 6 +-- ...OneWithoutRelationsRecordGqlFields.test.ts | 35 +++++++++++++++ .../utils/generateDepthOneRecordGqlFields.ts | 1 + ...DepthOneWithoutRelationsRecordGqlFields.ts | 19 ++++++++ .../useLazyLoadRecordIndexTable.test.tsx | 43 ++++++++++++++++++- .../hooks/useRecordBoardRecordGqlFields.ts | 28 +++--------- .../hooks/useRecordTableRecordGqlFields.ts | 26 +++-------- 7 files changed, 113 insertions(+), 45 deletions(-) create mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts create mode 100644 packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts diff --git a/packages/twenty-e2e-testing/tests/workflow-run.spec.ts b/packages/twenty-e2e-testing/tests/workflow-run.spec.ts index 70635fa27..21dccb661 100644 --- a/packages/twenty-e2e-testing/tests/workflow-run.spec.ts +++ b/packages/twenty-e2e-testing/tests/workflow-run.spec.ts @@ -40,9 +40,9 @@ test('The workflow run visualizer shows the executed draft version without the l await page.goto(executionPageUrl!); - const workflowRunName = page.getByText( - `#1 - ${workflowVisualizer.workflowName}`, - ); + const workflowRunName = page + .getByText(`#1 - ${workflowVisualizer.workflowName}`) + .nth(1); await expect(workflowRunName).toBeVisible(); diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts new file mode 100644 index 000000000..8bae151e4 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/utils/__tests__/generateDepthOneWithoutRelationsRecordGqlFields.test.ts @@ -0,0 +1,35 @@ +import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; +import { getMockPersonObjectMetadataItem } from '~/testing/mock-data/people'; + +describe('generateDepthOneWithoutRelationsRecordGqlFields', () => { + const objectMetadataItem = getMockPersonObjectMetadataItem(); + it('Should handle basic call with standalone objectMetadataItem', () => { + const result = generateDepthOneWithoutRelationsRecordGqlFields({ + objectMetadataItem, + }); + expect(result).toMatchInlineSnapshot(` +{ + "avatarUrl": true, + "city": true, + "companyId": true, + "createdAt": true, + "createdBy": true, + "deletedAt": true, + "emails": true, + "id": true, + "intro": true, + "jobTitle": true, + "linkedinLink": true, + "name": true, + "performanceRating": true, + "phones": true, + "position": true, + "searchVector": true, + "updatedAt": true, + "whatsapp": true, + "workPreference": true, + "xLink": true, +} +`); + }); +}); diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts index 0c76661bd..0e779f988 100644 --- a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneRecordGqlFields.ts @@ -3,6 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; export type GenerateDepthOneRecordGqlFields = { objectMetadataItem: ObjectMetadataItem; }; + export const generateDepthOneRecordGqlFields = ({ objectMetadataItem, }: GenerateDepthOneRecordGqlFields) => diff --git a/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts new file mode 100644 index 000000000..ff656176d --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields.ts @@ -0,0 +1,19 @@ +import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { FieldMetadataType } from '~/generated/graphql'; + +type GenerateDepthOneWithoutRelationsRecordGqlFields = { + objectMetadataItem: ObjectMetadataItem; +}; + +export const generateDepthOneWithoutRelationsRecordGqlFields = ({ + objectMetadataItem, +}: GenerateDepthOneWithoutRelationsRecordGqlFields) => { + return objectMetadataItem.fields + .filter((field) => field.type !== FieldMetadataType.RELATION) + .reduce>((acc, field) => { + return { + ...acc, + [field.name]: true, + }; + }, {}); +}; diff --git a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyLoadRecordIndexTable.test.tsx b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyLoadRecordIndexTable.test.tsx index f75cdb458..48d3354e2 100644 --- a/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyLoadRecordIndexTable.test.tsx +++ b/packages/twenty-front/src/modules/object-record/hooks/__tests__/useLazyLoadRecordIndexTable.test.tsx @@ -39,8 +39,28 @@ const mocks: MockedResponse[] = [ node { __typename avatarUrl + city + companyId + createdAt + createdBy { + source + workspaceMemberId + name + context + } deletedAt + emails { + primaryEmail + additionalEmails + } id + intro + jobTitle + linkedinLink { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } name { firstName lastName @@ -287,6 +307,13 @@ const mocks: MockedResponse[] = [ } } } + performanceRating + phones { + primaryPhoneNumber + primaryPhoneCountryCode + primaryPhoneCallingCode + additionalPhones + } position taskTargets { edges { @@ -533,6 +560,19 @@ const mocks: MockedResponse[] = [ } } } + updatedAt + whatsapp { + primaryPhoneNumber + primaryPhoneCountryCode + primaryPhoneCallingCode + additionalPhones + } + workPreference + xLink { + primaryLinkUrl + primaryLinkLabel + secondaryLinks + } } cursor } @@ -625,7 +665,6 @@ describe('useLazyLoadRecordIndexTable', () => { await result.current.findManyRecords(); }); - expect(Array.isArray(result.current.records)).toBe(true); - expect(result.current.records.length).toBe(16); + expect(result.current.records).toHaveLength(16); }); }); diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts index d7f5e70fc..857350144 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordBoardRecordGqlFields.ts @@ -1,9 +1,8 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields'; -import { hasObjectMetadataItemPositionField } from '@/object-metadata/utils/hasObjectMetadataItemPositionField'; import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; import { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; @@ -21,24 +20,11 @@ export const useRecordBoardRecordGqlFields = ({ recordBoardId, ); - const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } = - getObjectMetadataIdentifierFields({ objectMetadataItem }); - const recordGroupFieldMetadata = useRecoilComponentValueV2( recordGroupFieldMetadataComponentState, recordBoardId, ); - const identifierQueryFields: Record = {}; - - if (isDefined(labelIdentifierFieldMetadataItem)) { - identifierQueryFields[labelIdentifierFieldMetadataItem.name] = true; - } - - if (isDefined(imageIdentifierFieldMetadataItem)) { - identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true; - } - const { objectMetadataItem: noteTargetObjectMetadataItem } = useObjectMetadataItem({ objectNameSingular: CoreObjectNameSingular.NoteTarget, @@ -49,19 +35,19 @@ export const useRecordBoardRecordGqlFields = ({ objectNameSingular: CoreObjectNameSingular.TaskTarget, }); + const allDepthOneWithoutRelationsRecordGqlFields = + generateDepthOneWithoutRelationsRecordGqlFields({ + objectMetadataItem, + }); + const recordGqlFields: Record = { - id: true, - deletedAt: true, + ...allDepthOneWithoutRelationsRecordGqlFields, ...Object.fromEntries( visibleFieldDefinitions.map((visibleFieldDefinition) => [ visibleFieldDefinition.metadata.fieldName, true, ]), ), - ...(hasObjectMetadataItemPositionField(objectMetadataItem) - ? { position: true } - : undefined), - ...identifierQueryFields, noteTargets: generateDepthOneRecordGqlFields({ objectMetadataItem: noteTargetObjectMetadataItem, }), diff --git a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts index fcb9092cb..fc25392e4 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts +++ b/packages/twenty-front/src/modules/object-record/record-index/hooks/useRecordTableRecordGqlFields.ts @@ -1,34 +1,20 @@ import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; -import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields'; import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; +import { generateDepthOneWithoutRelationsRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneWithoutRelationsRecordGqlFields'; import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { isDefined } from 'twenty-shared/utils'; export const useRecordTableRecordGqlFields = ({ objectMetadataItem, }: { objectMetadataItem: ObjectMetadataItem; }) => { - const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } = - getObjectMetadataIdentifierFields({ objectMetadataItem }); - const visibleTableColumns = useRecoilComponentValueV2( visibleTableColumnsComponentSelector, ); - const identifierQueryFields: Record = {}; - - if (isDefined(labelIdentifierFieldMetadataItem)) { - identifierQueryFields[labelIdentifierFieldMetadataItem.name] = true; - } - - if (isDefined(imageIdentifierFieldMetadataItem)) { - identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true; - } - const { objectMetadataItem: noteTargetObjectMetadataItem } = useObjectMetadataItem({ objectNameSingular: CoreObjectNameSingular.NoteTarget, @@ -39,14 +25,16 @@ export const useRecordTableRecordGqlFields = ({ objectNameSingular: CoreObjectNameSingular.TaskTarget, }); + const allDepthOneWithoutRelationsRecordGqlFields = + generateDepthOneWithoutRelationsRecordGqlFields({ + objectMetadataItem, + }); + const recordGqlFields: Record = { - id: true, - deletedAt: true, + ...allDepthOneWithoutRelationsRecordGqlFields, ...Object.fromEntries( visibleTableColumns.map((column) => [column.metadata.fieldName, true]), ), - ...identifierQueryFields, - position: true, noteTargets: generateDepthOneRecordGqlFields({ objectMetadataItem: noteTargetObjectMetadataItem, }),