diff --git a/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts b/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts index c8637e77cb..2db1e69035 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useActivityTargetsForTargetableObjects.ts @@ -28,12 +28,14 @@ export const useActivityTargetsForTargetableObjects = ({ activityTargetsOrderByVariables: RecordGqlOperationOrderBy; limit: number; }) => { + const objectMetadataItems = useRecoilValue(objectMetadataItemsState); + const activityTargetsFilter = getActivityTargetsFilter({ targetableObjects: targetableObjects, + activityObjectNameSingular: objectNameSingular, + objectMetadataItems, }); - const objectMetadataItems = useRecoilValue(objectMetadataItemsState); - const FIND_ACTIVITY_TARGETS_OPERATION_SIGNATURE = findActivityTargetsOperationSignatureFactory({ objectNameSingular, diff --git a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts index 58f58c628d..200fa450d9 100644 --- a/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts +++ b/packages/twenty-front/src/modules/activities/hooks/useOpenCreateActivityDrawer.ts @@ -13,7 +13,9 @@ import { type NoteTarget } from '@/activities/types/NoteTarget'; import { type Task } from '@/activities/types/Task'; import { type TaskTarget } from '@/activities/types/TaskTarget'; import { useOpenRecordInCommandMenu } from '@/command-menu/hooks/useOpenRecordInCommandMenu'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; import { useCreateOneRecord } from '@/object-record/hooks/useCreateOneRecord'; +import { isDefined } from 'twenty-shared/utils'; export const useOpenCreateActivityDrawer = ({ activityObjectNameSingular, @@ -52,6 +54,8 @@ export const useOpenCreateActivityDrawer = ({ const { openRecordInCommandMenu } = useOpenRecordInCommandMenu(); + const { objectMetadataItems } = useObjectMetadataItems(); + const openCreateActivityDrawer = async ({ targetableObjects, customAssignee, @@ -72,7 +76,31 @@ export const useOpenCreateActivityDrawer = ({ }); if (targetableObjects.length > 0) { - const targetableObjectRelationIdName = `${targetableObjects[0].targetObjectNameSingular}Id`; + const activityTargetObjectNameSingular = + activityObjectNameSingular === CoreObjectNameSingular.Task + ? CoreObjectNameSingular.TaskTarget + : CoreObjectNameSingular.NoteTarget; + + const activityTargetObjectMetadata = objectMetadataItems.find( + (item) => item.nameSingular === activityTargetObjectNameSingular, + ); + + const targetObjectMetadataItem = objectMetadataItems.find( + (item) => + item.nameSingular === targetableObjects[0].targetObjectNameSingular, + ); + + const targetField = activityTargetObjectMetadata?.fields.find( + (field) => + field.relation?.targetObjectMetadata.id === + targetObjectMetadataItem?.id, + ); + + const joinColumnName = targetField?.settings?.joinColumnName; + + const targetableObjectRelationIdName = isDefined(joinColumnName) + ? joinColumnName + : `${targetableObjects[0].targetObjectNameSingular}Id`; await createOneActivityTarget({ ...(activityObjectNameSingular === CoreObjectNameSingular.Task diff --git a/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts b/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts index d94417c3a0..6b777cbcf8 100644 --- a/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts +++ b/packages/twenty-front/src/modules/activities/inline-cell/hooks/useUpdateActivityTargetFromCell.ts @@ -1,6 +1,7 @@ import { type ActivityTargetWithTargetRecord } from '@/activities/types/ActivityTargetObject'; import { type NoteTarget } from '@/activities/types/NoteTarget'; import { type TaskTarget } from '@/activities/types/TaskTarget'; +import { getActivityTargetFieldNameForObject } from '@/activities/utils/getActivityTargetFieldNameForObject'; import { getJoinObjectNameSingular } from '@/activities/utils/getJoinObjectNameSingular'; import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; @@ -65,18 +66,31 @@ export const useUpdateActivityTargetFromCell = ({ ? 'task' : 'note'; - const pickedObjectMetadataItem = snapshot + const objectMetadataItems = snapshot .getLoadable(objectMetadataItemsState) - .getValue() - .find( - (objectMetadataItem) => - objectMetadataItem.id === morphItem.objectMetadataId, - ); + .getValue(); + + const pickedObjectMetadataItem = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.id === morphItem.objectMetadataId, + ); if (!isDefined(pickedObjectMetadataItem)) { throw new Error('Could not find object metadata item'); } + const targetFieldName = getActivityTargetFieldNameForObject({ + activityObjectNameSingular, + targetObjectMetadataId: morphItem.objectMetadataId, + objectMetadataItems, + }); + + if (!isDefined(targetFieldName)) { + throw new Error( + `Could not find field on activity target for object ${pickedObjectMetadataItem.nameSingular}`, + ); + } + let activityTargetsAfterUpdate: (TaskTarget | NoteTarget)[] = []; const existingActivityTarget = activityTargetWithTargetRecords.find( @@ -132,7 +146,7 @@ export const useUpdateActivityTargetFromCell = ({ createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }, - [pickedObjectMetadataItem.nameSingular]: targetRecord, + [targetFieldName]: targetRecord, } : { id: v4(), @@ -146,7 +160,7 @@ export const useUpdateActivityTargetFromCell = ({ createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }, - [pickedObjectMetadataItem.nameSingular]: targetRecord, + [targetFieldName]: targetRecord, }; activityTargetsAfterUpdate = [ @@ -159,8 +173,8 @@ export const useUpdateActivityTargetFromCell = ({ await createOneActivityTarget({ ...activityTarget, [targetObjectName]: undefined, - [pickedObjectMetadataItem.nameSingular]: undefined, - [`${pickedObjectMetadataItem.nameSingular}Id`]: morphItem.recordId, + [targetFieldName]: undefined, + [`${targetFieldName}Id`]: morphItem.recordId, } as Partial); } diff --git a/packages/twenty-front/src/modules/activities/utils/getActivityTargetFieldNameForObject.ts b/packages/twenty-front/src/modules/activities/utils/getActivityTargetFieldNameForObject.ts new file mode 100644 index 0000000000..9793c5fa7b --- /dev/null +++ b/packages/twenty-front/src/modules/activities/utils/getActivityTargetFieldNameForObject.ts @@ -0,0 +1,38 @@ +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { isDefined } from 'twenty-shared/utils'; + +type GetActivityTargetFieldNameForObjectArgs = { + activityObjectNameSingular: + | CoreObjectNameSingular.Note + | CoreObjectNameSingular.Task; + targetObjectMetadataId: string; + objectMetadataItems: ObjectMetadataItem[]; +}; + +export const getActivityTargetFieldNameForObject = ({ + activityObjectNameSingular, + targetObjectMetadataId, + objectMetadataItems, +}: GetActivityTargetFieldNameForObjectArgs): string | undefined => { + const activityTargetObjectNameSingular = + activityObjectNameSingular === CoreObjectNameSingular.Task + ? CoreObjectNameSingular.TaskTarget + : CoreObjectNameSingular.NoteTarget; + + const activityTargetObjectMetadata = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.nameSingular === activityTargetObjectNameSingular, + ); + + if (!isDefined(activityTargetObjectMetadata)) { + return undefined; + } + + const targetField = activityTargetObjectMetadata.fields.find( + (field) => + field.relation?.targetObjectMetadata.id === targetObjectMetadataId, + ); + + return targetField?.name; +}; diff --git a/packages/twenty-front/src/modules/activities/utils/getActivityTargetObjectRecords.ts b/packages/twenty-front/src/modules/activities/utils/getActivityTargetObjectRecords.ts index 8623abb8c5..802ce23397 100644 --- a/packages/twenty-front/src/modules/activities/utils/getActivityTargetObjectRecords.ts +++ b/packages/twenty-front/src/modules/activities/utils/getActivityTargetObjectRecords.ts @@ -24,6 +24,8 @@ export const getActivityTargetObjectRecords = ({ return []; } + const isNote = isDefined(activityRecord) && 'noteTargets' in activityRecord; + const targets = activityTargets ? activityTargets : activityRecord && @@ -36,27 +38,50 @@ export const getActivityTargetObjectRecords = ({ ? activityRecord.taskTargets : []; + const activityTargetObjectNameSingular = isNote + ? CoreObjectNameSingular.NoteTarget + : CoreObjectNameSingular.TaskTarget; + + const activityTargetObjectMetadata = objectMetadataItems.find( + (item) => item.nameSingular === activityTargetObjectNameSingular, + ); + + const activityTargetRelationFields = + activityTargetObjectMetadata?.fields.filter( + (field) => + isDefined(field.relation?.targetObjectMetadata.id) && + ![CoreObjectNameSingular.Note, CoreObjectNameSingular.Task].includes( + field.relation?.targetObjectMetadata + .nameSingular as CoreObjectNameSingular, + ), + ) ?? []; + const activityTargetObjectRecords = targets .map((activityTarget) => { if (!isDefined(activityTarget)) { throw new Error('Cannot find activity target'); } + const matchingField = activityTargetRelationFields.find((field) => + isDefined(activityTarget[field.name]), + ); + + if (!matchingField || !matchingField.relation) { + return undefined; + } const correspondingObjectMetadataItem = objectMetadataItems.find( (objectMetadataItem) => - isDefined(activityTarget[objectMetadataItem.nameSingular]) && - ![CoreObjectNameSingular.Note, CoreObjectNameSingular.Task].includes( - objectMetadataItem.nameSingular as CoreObjectNameSingular, - ), + objectMetadataItem.id === + matchingField.relation?.targetObjectMetadata.id, ); if (!correspondingObjectMetadataItem) { return undefined; } - const targetObjectRecord = activityTarget[ - correspondingObjectMetadataItem.nameSingular - ] as ObjectRecord | undefined; + const targetObjectRecord = activityTarget[matchingField.name] as + | ObjectRecord + | undefined; if (!isDefined(targetObjectRecord)) { throw new Error( diff --git a/packages/twenty-front/src/modules/activities/utils/getActivityTargetsFilter.ts b/packages/twenty-front/src/modules/activities/utils/getActivityTargetsFilter.ts index 9b2fdf1c63..b73b724473 100644 --- a/packages/twenty-front/src/modules/activities/utils/getActivityTargetsFilter.ts +++ b/packages/twenty-front/src/modules/activities/utils/getActivityTargetsFilter.ts @@ -1,25 +1,62 @@ import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; -import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { type ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; +import { isDefined } from 'twenty-shared/utils'; export const getActivityTargetsFilter = ({ targetableObjects, + activityObjectNameSingular, + objectMetadataItems, }: { targetableObjects: ActivityTargetableObject[]; + activityObjectNameSingular: + | CoreObjectNameSingular.Note + | CoreObjectNameSingular.Task; + objectMetadataItems: ObjectMetadataItem[]; }) => { - const findManyActivitiyTargetsQueryFilter = Object.fromEntries( - targetableObjects.map((targetableObject) => { - const targetObjectFieldName = getActivityTargetObjectFieldIdName({ - nameSingular: targetableObject.targetObjectNameSingular, - }); + const activityTargetObjectNameSingular = + activityObjectNameSingular === CoreObjectNameSingular.Task + ? CoreObjectNameSingular.TaskTarget + : CoreObjectNameSingular.NoteTarget; - return [ - targetObjectFieldName, - { - eq: targetableObject.id, - }, - ]; - }), + const activityTargetObjectMetadata = objectMetadataItems.find( + (objectMetadataItem) => + objectMetadataItem.nameSingular === activityTargetObjectNameSingular, ); - return findManyActivitiyTargetsQueryFilter; + const findManyActivityTargetsQueryFilter = Object.fromEntries( + targetableObjects + .map((targetableObject) => { + const targetObjectMetadataItem = objectMetadataItems.find( + (item) => + item.nameSingular === targetableObject.targetObjectNameSingular, + ); + + if (!isDefined(targetObjectMetadataItem)) { + return undefined; + } + + const targetField = activityTargetObjectMetadata?.fields.find( + (field) => + field.relation?.targetObjectMetadata.id === + targetObjectMetadataItem.id, + ); + + const joinColumnName = targetField?.settings?.joinColumnName; + + if (!isDefined(joinColumnName)) { + return undefined; + } + + return [ + joinColumnName, + { + eq: targetableObject.id, + }, + ]; + }) + .filter(isDefined), + ); + + return findManyActivityTargetsQueryFilter; }; diff --git a/packages/twenty-front/src/modules/activities/utils/getActivityTargetsToCreateFromTargetableObjects.ts b/packages/twenty-front/src/modules/activities/utils/getActivityTargetsToCreateFromTargetableObjects.ts deleted file mode 100644 index 3d086397df..0000000000 --- a/packages/twenty-front/src/modules/activities/utils/getActivityTargetsToCreateFromTargetableObjects.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { v4 } from 'uuid'; - -import { type ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; -import { type Note } from '@/activities/types/Note'; -import { type NoteTarget } from '@/activities/types/NoteTarget'; -import { type Task } from '@/activities/types/Task'; -import { type TaskTarget } from '@/activities/types/TaskTarget'; -import { getActivityTargetObjectFieldIdName } from '@/activities/utils/getActivityTargetObjectFieldIdName'; -import { type ObjectRecord } from '@/object-record/types/ObjectRecord'; - -export const makeActivityTargetsToCreateFromTargetableObjects = ({ - targetableObjects, - activity, - targetObjectRecords, -}: { - targetableObjects: ActivityTargetableObject[]; - activity: Task | Note; - targetObjectRecords: ObjectRecord[]; -}): Partial[] => { - const activityTargetsToCreate = targetableObjects.map((targetableObject) => { - const targetableObjectFieldIdName = getActivityTargetObjectFieldIdName({ - nameSingular: targetableObject.targetObjectNameSingular, - }); - - const relatedObjectRecord = targetObjectRecords.find( - (record) => record.id === targetableObject.id, - ); - - const activityTarget = { - [targetableObject.targetObjectNameSingular]: relatedObjectRecord, - [targetableObjectFieldIdName]: targetableObject.id, - activity, - activityId: activity.id, - id: v4(), - updatedAt: new Date().toISOString(), - createdAt: new Date().toISOString(), - } as Partial; - - return activityTarget; - }); - - return activityTargetsToCreate; -};