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.
This commit is contained in:
Baptiste Devessier
2025-04-22 16:45:36 +02:00
committed by GitHub
parent 0b7024c94a
commit efab98a8f8
7 changed files with 113 additions and 45 deletions

View File

@ -40,9 +40,9 @@ test('The workflow run visualizer shows the executed draft version without the l
await page.goto(executionPageUrl!); await page.goto(executionPageUrl!);
const workflowRunName = page.getByText( const workflowRunName = page
`#1 - ${workflowVisualizer.workflowName}`, .getByText(`#1 - ${workflowVisualizer.workflowName}`)
); .nth(1);
await expect(workflowRunName).toBeVisible(); await expect(workflowRunName).toBeVisible();

View File

@ -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,
}
`);
});
});

View File

@ -3,6 +3,7 @@ import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export type GenerateDepthOneRecordGqlFields = { export type GenerateDepthOneRecordGqlFields = {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
}; };
export const generateDepthOneRecordGqlFields = ({ export const generateDepthOneRecordGqlFields = ({
objectMetadataItem, objectMetadataItem,
}: GenerateDepthOneRecordGqlFields) => }: GenerateDepthOneRecordGqlFields) =>

View File

@ -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<Record<string, true>>((acc, field) => {
return {
...acc,
[field.name]: true,
};
}, {});
};

View File

@ -39,8 +39,28 @@ const mocks: MockedResponse[] = [
node { node {
__typename __typename
avatarUrl avatarUrl
city
companyId
createdAt
createdBy {
source
workspaceMemberId
name
context
}
deletedAt deletedAt
emails {
primaryEmail
additionalEmails
}
id id
intro
jobTitle
linkedinLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
name { name {
firstName firstName
lastName lastName
@ -287,6 +307,13 @@ const mocks: MockedResponse[] = [
} }
} }
} }
performanceRating
phones {
primaryPhoneNumber
primaryPhoneCountryCode
primaryPhoneCallingCode
additionalPhones
}
position position
taskTargets { taskTargets {
edges { edges {
@ -533,6 +560,19 @@ const mocks: MockedResponse[] = [
} }
} }
} }
updatedAt
whatsapp {
primaryPhoneNumber
primaryPhoneCountryCode
primaryPhoneCallingCode
additionalPhones
}
workPreference
xLink {
primaryLinkUrl
primaryLinkLabel
secondaryLinks
}
} }
cursor cursor
} }
@ -625,7 +665,6 @@ describe('useLazyLoadRecordIndexTable', () => {
await result.current.findManyRecords(); await result.current.findManyRecords();
}); });
expect(Array.isArray(result.current.records)).toBe(true); expect(result.current.records).toHaveLength(16);
expect(result.current.records.length).toBe(16);
}); });
}); });

View File

@ -1,9 +1,8 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; 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 { 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 { recordBoardVisibleFieldDefinitionsComponentSelector } from '@/object-record/record-board/states/selectors/recordBoardVisibleFieldDefinitionsComponentSelector';
import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState'; import { recordGroupFieldMetadataComponentState } from '@/object-record/record-group/states/recordGroupFieldMetadataComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
@ -21,24 +20,11 @@ export const useRecordBoardRecordGqlFields = ({
recordBoardId, recordBoardId,
); );
const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } =
getObjectMetadataIdentifierFields({ objectMetadataItem });
const recordGroupFieldMetadata = useRecoilComponentValueV2( const recordGroupFieldMetadata = useRecoilComponentValueV2(
recordGroupFieldMetadataComponentState, recordGroupFieldMetadataComponentState,
recordBoardId, recordBoardId,
); );
const identifierQueryFields: Record<string, boolean> = {};
if (isDefined(labelIdentifierFieldMetadataItem)) {
identifierQueryFields[labelIdentifierFieldMetadataItem.name] = true;
}
if (isDefined(imageIdentifierFieldMetadataItem)) {
identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true;
}
const { objectMetadataItem: noteTargetObjectMetadataItem } = const { objectMetadataItem: noteTargetObjectMetadataItem } =
useObjectMetadataItem({ useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.NoteTarget, objectNameSingular: CoreObjectNameSingular.NoteTarget,
@ -49,19 +35,19 @@ export const useRecordBoardRecordGqlFields = ({
objectNameSingular: CoreObjectNameSingular.TaskTarget, objectNameSingular: CoreObjectNameSingular.TaskTarget,
}); });
const allDepthOneWithoutRelationsRecordGqlFields =
generateDepthOneWithoutRelationsRecordGqlFields({
objectMetadataItem,
});
const recordGqlFields: Record<string, any> = { const recordGqlFields: Record<string, any> = {
id: true, ...allDepthOneWithoutRelationsRecordGqlFields,
deletedAt: true,
...Object.fromEntries( ...Object.fromEntries(
visibleFieldDefinitions.map((visibleFieldDefinition) => [ visibleFieldDefinitions.map((visibleFieldDefinition) => [
visibleFieldDefinition.metadata.fieldName, visibleFieldDefinition.metadata.fieldName,
true, true,
]), ]),
), ),
...(hasObjectMetadataItemPositionField(objectMetadataItem)
? { position: true }
: undefined),
...identifierQueryFields,
noteTargets: generateDepthOneRecordGqlFields({ noteTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: noteTargetObjectMetadataItem, objectMetadataItem: noteTargetObjectMetadataItem,
}), }),

View File

@ -1,34 +1,20 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { getObjectMetadataIdentifierFields } from '@/object-metadata/utils/getObjectMetadataIdentifierFields';
import { generateDepthOneRecordGqlFields } from '@/object-record/graphql/utils/generateDepthOneRecordGqlFields'; 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 { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { isDefined } from 'twenty-shared/utils';
export const useRecordTableRecordGqlFields = ({ export const useRecordTableRecordGqlFields = ({
objectMetadataItem, objectMetadataItem,
}: { }: {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
}) => { }) => {
const { imageIdentifierFieldMetadataItem, labelIdentifierFieldMetadataItem } =
getObjectMetadataIdentifierFields({ objectMetadataItem });
const visibleTableColumns = useRecoilComponentValueV2( const visibleTableColumns = useRecoilComponentValueV2(
visibleTableColumnsComponentSelector, visibleTableColumnsComponentSelector,
); );
const identifierQueryFields: Record<string, boolean> = {};
if (isDefined(labelIdentifierFieldMetadataItem)) {
identifierQueryFields[labelIdentifierFieldMetadataItem.name] = true;
}
if (isDefined(imageIdentifierFieldMetadataItem)) {
identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true;
}
const { objectMetadataItem: noteTargetObjectMetadataItem } = const { objectMetadataItem: noteTargetObjectMetadataItem } =
useObjectMetadataItem({ useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.NoteTarget, objectNameSingular: CoreObjectNameSingular.NoteTarget,
@ -39,14 +25,16 @@ export const useRecordTableRecordGqlFields = ({
objectNameSingular: CoreObjectNameSingular.TaskTarget, objectNameSingular: CoreObjectNameSingular.TaskTarget,
}); });
const allDepthOneWithoutRelationsRecordGqlFields =
generateDepthOneWithoutRelationsRecordGqlFields({
objectMetadataItem,
});
const recordGqlFields: Record<string, any> = { const recordGqlFields: Record<string, any> = {
id: true, ...allDepthOneWithoutRelationsRecordGqlFields,
deletedAt: true,
...Object.fromEntries( ...Object.fromEntries(
visibleTableColumns.map((column) => [column.metadata.fieldName, true]), visibleTableColumns.map((column) => [column.metadata.fieldName, true]),
), ),
...identifierQueryFields,
position: true,
noteTargets: generateDepthOneRecordGqlFields({ noteTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: noteTargetObjectMetadataItem, objectMetadataItem: noteTargetObjectMetadataItem,
}), }),