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!);
const workflowRunName = page.getByText(
`#1 - ${workflowVisualizer.workflowName}`,
);
const workflowRunName = page
.getByText(`#1 - ${workflowVisualizer.workflowName}`)
.nth(1);
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 = {
objectMetadataItem: ObjectMetadataItem;
};
export const generateDepthOneRecordGqlFields = ({
objectMetadataItem,
}: 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 {
__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);
});
});

View File

@ -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<string, boolean> = {};
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<string, any> = {
id: true,
deletedAt: true,
...allDepthOneWithoutRelationsRecordGqlFields,
...Object.fromEntries(
visibleFieldDefinitions.map((visibleFieldDefinition) => [
visibleFieldDefinition.metadata.fieldName,
true,
]),
),
...(hasObjectMetadataItemPositionField(objectMetadataItem)
? { position: true }
: undefined),
...identifierQueryFields,
noteTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: noteTargetObjectMetadataItem,
}),

View File

@ -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<string, boolean> = {};
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<string, any> = {
id: true,
deletedAt: true,
...allDepthOneWithoutRelationsRecordGqlFields,
...Object.fromEntries(
visibleTableColumns.map((column) => [column.metadata.fieldName, true]),
),
...identifierQueryFields,
position: true,
noteTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: noteTargetObjectMetadataItem,
}),