Add relations to notes/tasks list view (#6971)

<img width="664" alt="Screenshot 2024-09-10 at 17 00 11"
src="https://github.com/user-attachments/assets/37132805-ff67-4d28-b664-b03da680e166">

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Félix Malfait
2024-09-12 10:50:49 +02:00
committed by GitHub
parent 725ee837f9
commit f8e5b333d9
12 changed files with 135 additions and 80 deletions

View File

@ -1,3 +1,7 @@
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
import { NoteTarget } from '@/activities/types/NoteTarget';
import { TaskTarget } from '@/activities/types/TaskTarget';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { RecordChip } from '@/object-record/components/RecordChip';
import { useFieldFocus } from '@/object-record/record-field/hooks/useFieldFocus';
import { useRelationFromManyFieldDisplay } from '@/object-record/record-field/meta-types/hooks/useRelationFromManyFieldDisplay';
@ -7,24 +11,74 @@ export const RelationFromManyFieldDisplay = () => {
const { fieldValue, fieldDefinition } = useRelationFromManyFieldDisplay();
const { isFocused } = useFieldFocus();
const { fieldName, objectMetadataNameSingular } = fieldDefinition.metadata;
const relationObjectNameSingular =
fieldDefinition?.metadata.relationObjectMetadataNameSingular;
const { activityTargetObjectRecords } = useActivityTargetObjectRecords(
undefined,
fieldValue as NoteTarget[] | TaskTarget[],
);
if (!fieldValue || !relationObjectNameSingular) {
return null;
}
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{fieldValue.map((record) => {
return (
const isRelationFromActivityTargets =
(fieldName === 'noteTargets' &&
objectMetadataNameSingular === CoreObjectNameSingular.Note) ||
(fieldName === 'taskTargets' &&
objectMetadataNameSingular === CoreObjectNameSingular.Task);
const isRelationFromManyActivities =
(fieldName === 'noteTargets' &&
objectMetadataNameSingular !== CoreObjectNameSingular.Note) ||
(fieldName === 'taskTargets' &&
objectMetadataNameSingular !== CoreObjectNameSingular.Task);
if (isRelationFromManyActivities) {
const objectNameSingular =
fieldName === 'noteTargets'
? CoreObjectNameSingular.Note
: CoreObjectNameSingular.Task;
const relationFieldName = fieldName === 'noteTargets' ? 'note' : 'task';
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{fieldValue.map((record) => (
<RecordChip
key={record.id}
objectNameSingular={objectNameSingular}
record={record[relationFieldName]}
/>
))}
</ExpandableList>
);
} else if (isRelationFromActivityTargets) {
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{activityTargetObjectRecords.map((record) => (
<RecordChip
key={record.targetObject.id}
objectNameSingular={record.targetObjectMetadataItem.nameSingular}
record={record.targetObject}
/>
))}
</ExpandableList>
);
} else {
return (
<ExpandableList isChipCountDisplayed={isFocused}>
{fieldValue.map((record) => (
<RecordChip
key={record.id}
objectNameSingular={relationObjectNameSingular}
record={record}
/>
);
})}
</ExpandableList>
);
))}
</ExpandableList>
);
}
};

View File

@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { Meta, StoryObj } from '@storybook/react';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { ComponentDecorator } from 'twenty-ui';
@ -94,9 +94,10 @@ type Story = StoryObj<typeof RelationFromManyFieldDisplay>;
export const Default: Story = {};
// TODO: optimize this component once we have morph many
export const Performance = getProfilingStory({
componentName: 'RelationFromManyFieldDisplay',
averageThresholdInMs: 0.5,
averageThresholdInMs: 1,
numberOfRuns: 20,
numberOfTestsPerRun: 100,
});

View File

@ -1,7 +1,10 @@
import { useRecoilValue } from 'recoil';
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 { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
import { isDefined } from '~/utils/isDefined';
@ -27,6 +30,16 @@ export const useRecordTableRecordGqlFields = ({
identifierQueryFields[imageIdentifierFieldMetadataItem.name] = true;
}
const { objectMetadataItem: noteTargetObjectMetadataItem } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.NoteTarget,
});
const { objectMetadataItem: taskTargetObjectMetadataItem } =
useObjectMetadataItem({
objectNameSingular: CoreObjectNameSingular.TaskTarget,
});
const recordGqlFields: Record<string, any> = {
id: true,
...Object.fromEntries(
@ -34,18 +47,12 @@ export const useRecordTableRecordGqlFields = ({
),
...identifierQueryFields,
position: true,
noteTargets: {
note: {
id: true,
title: true,
},
},
taskTargets: {
task: {
id: true,
title: true,
},
},
noteTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: noteTargetObjectMetadataItem,
}),
taskTargets: generateDepthOneRecordGqlFields({
objectMetadataItem: taskTargetObjectMetadataItem,
}),
};
return recordGqlFields;