diff --git a/packages/twenty-front/src/modules/activities/comment/CommentHeader.tsx b/packages/twenty-front/src/modules/activities/comment/CommentHeader.tsx
index ff68c5332..a8cbb6b19 100644
--- a/packages/twenty-front/src/modules/activities/comment/CommentHeader.tsx
+++ b/packages/twenty-front/src/modules/activities/comment/CommentHeader.tsx
@@ -67,8 +67,8 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
const showDate = beautifiedCreatedAt !== '';
const author = comment.author;
- const authorName = author.name.firstName + ' ' + author.name.lastName;
- const avatarUrl = author.avatarUrl;
+ const authorName = author?.name?.firstName + ' ' + author?.name?.lastName;
+ const avatarUrl = author?.avatarUrl;
const commentId = comment.id;
return (
@@ -77,7 +77,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
{authorName}
diff --git a/packages/twenty-front/src/modules/activities/components/ActivityComments.tsx b/packages/twenty-front/src/modules/activities/components/ActivityComments.tsx
index 001c402eb..9d88bcc27 100644
--- a/packages/twenty-front/src/modules/activities/components/ActivityComments.tsx
+++ b/packages/twenty-front/src/modules/activities/components/ActivityComments.tsx
@@ -86,6 +86,7 @@ export const ActivityComments = ({
createOneComment?.({
id: v4(),
authorId: currentWorkspaceMember?.id ?? '',
+ author: currentWorkspaceMember,
activityId: activity?.id ?? '',
body: commentText,
createdAt: new Date().toISOString(),
diff --git a/packages/twenty-front/src/modules/activities/hooks/useActivityTargets.ts b/packages/twenty-front/src/modules/activities/hooks/useActivityTargets.ts
index eb8845073..11757793c 100644
--- a/packages/twenty-front/src/modules/activities/hooks/useActivityTargets.ts
+++ b/packages/twenty-front/src/modules/activities/hooks/useActivityTargets.ts
@@ -20,6 +20,7 @@ export const useActivityTargets = ({
eq: targetableObject.id,
},
},
+ skip: !targetableObject.id,
});
return {
diff --git a/packages/twenty-front/src/modules/activities/tasks/hooks/useTasks.ts b/packages/twenty-front/src/modules/activities/tasks/hooks/useTasks.ts
index d411b8350..292cec94b 100644
--- a/packages/twenty-front/src/modules/activities/tasks/hooks/useTasks.ts
+++ b/packages/twenty-front/src/modules/activities/tasks/hooks/useTasks.ts
@@ -24,7 +24,6 @@ export const useTasks = ({
});
const isTargettingObjectRecords = isNonEmptyArray(targetableObjects);
-
const targetableObjectsFilter =
targetableObjects.reduce(
(aggregateFilter, targetableObject) => {
diff --git a/packages/twenty-front/src/modules/activities/utils/getTargetObjectFilterFieldName.ts b/packages/twenty-front/src/modules/activities/utils/getTargetObjectFilterFieldName.ts
index be1256229..6932fbf12 100644
--- a/packages/twenty-front/src/modules/activities/utils/getTargetObjectFilterFieldName.ts
+++ b/packages/twenty-front/src/modules/activities/utils/getTargetObjectFilterFieldName.ts
@@ -1,15 +1,7 @@
-import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
-
export const getActivityTargetObjectFieldIdName = ({
nameSingular,
}: {
nameSingular: string;
}) => {
- const isCoreObject =
- nameSingular === CoreObjectNameSingular.Company ||
- nameSingular === CoreObjectNameSingular.Person;
-
- const objectFieldIdName = `${!isCoreObject ? '_' : ''}${nameSingular}Id`;
-
- return objectFieldIdName;
+ return `${nameSingular}Id`;
};
diff --git a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
index cc98a87bc..f006541bb 100644
--- a/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
+++ b/packages/twenty-front/src/modules/favorites/hooks/useFavorites.ts
@@ -91,12 +91,12 @@ export const useFavorites = () => {
]);
const createFavorite = (
- targetObject: Record,
+ targetRecord: Record,
targetObjectNameSingular: string,
) => {
createOneFavorite({
- [`${targetObjectNameSingular}Id`]: targetObject.id,
- [`${targetObjectNameSingular}`]: targetObject,
+ [`${targetObjectNameSingular}Id`]: targetRecord.id,
+ [`${targetObjectNameSingular}`]: targetRecord,
position: favorites.length + 1,
workspaceMemberId: currentWorkspaceMember?.id,
});
diff --git a/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts b/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts
index 14e223f91..60ed256d0 100644
--- a/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts
+++ b/packages/twenty-server/src/database/typeorm-seeds/core/demo/feature-flags.ts
@@ -26,12 +26,12 @@ export const seedFeatureFlags = async (
{
key: 'IS_RATING_FIELD_TYPE_ENABLED',
workspaceId: workspaceId,
- value: true,
+ value: false,
},
{
key: 'IS_RELATION_FIELD_CARD_ENABLED',
workspaceId: workspaceId,
- value: true,
+ value: false,
},
])
.execute();
diff --git a/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts b/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts
index fe90be30c..9cafcb64e 100644
--- a/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts
+++ b/packages/twenty-server/src/metadata/object-metadata/object-metadata.service.ts
@@ -141,88 +141,16 @@ export class ObjectMetadataService extends TypeOrmQueryService {
- if (fieldMetadata.type === FieldMetadataType.RELATION) {
- acc[fieldMetadata.objectMetadataId] = fieldMetadata;
- }
-
- return acc;
- },
- {},
+ const { activityTargetObjectMetadata } =
+ await this.createActivityTargetRelation(
+ objectMetadataInput.workspaceId,
+ createdObjectMetadata,
);
- await this.relationMetadataRepository.save([
- {
- workspaceId: objectMetadataInput.workspaceId,
- relationType: RelationMetadataType.ONE_TO_MANY,
- fromObjectMetadataId: createdObjectMetadata.id,
- toObjectMetadataId: activityTargetObjectMetadata.id,
- fromFieldMetadataId:
- activityTargetRelationFieldMetadataMap[createdObjectMetadata.id].id,
- toFieldMetadataId:
- activityTargetRelationFieldMetadataMap[
- activityTargetObjectMetadata.id
- ].id,
- },
- ]);
+ const { favoriteObjectMetadata } = await this.createFavoriteRelation(
+ objectMetadataInput.workspaceId,
+ createdObjectMetadata,
+ );
await this.workspaceMigrationService.createCustomMigration(
createdObjectMetadata.workspaceId,
@@ -256,6 +184,31 @@ export class ObjectMetadataService extends TypeOrmQueryService {
+ if (fieldMetadata.type === FieldMetadataType.RELATION) {
+ acc[fieldMetadata.objectMetadataId] = fieldMetadata;
+ }
+
+ return acc;
+ },
+ {},
+ );
+
+ await this.relationMetadataRepository.save([
+ {
+ workspaceId: workspaceId,
+ relationType: RelationMetadataType.ONE_TO_MANY,
+ fromObjectMetadataId: createdObjectMetadata.id,
+ toObjectMetadataId: activityTargetObjectMetadata.id,
+ fromFieldMetadataId:
+ activityTargetRelationFieldMetadataMap[createdObjectMetadata.id].id,
+ toFieldMetadataId:
+ activityTargetRelationFieldMetadataMap[
+ activityTargetObjectMetadata.id
+ ].id,
+ },
+ ]);
+
+ return { activityTargetObjectMetadata };
+ }
+
+ private async createFavoriteRelation(
+ workspaceId: string,
+ createdObjectMetadata: ObjectMetadataEntity,
+ ) {
+ const favoriteObjectMetadata =
+ await this.objectMetadataRepository.findOneByOrFail({
+ nameSingular: 'favorite',
+ workspaceId: workspaceId,
+ });
+
+ const favoriteRelationFieldMetadata =
+ await this.fieldMetadataRepository.save([
+ // FROM
+ {
+ objectMetadataId: createdObjectMetadata.id,
+ workspaceId: workspaceId,
+ isCustom: true,
+ isActive: true,
+ type: FieldMetadataType.RELATION,
+ name: 'favorites',
+ label: 'Favorites',
+ targetColumnMap: {},
+ description: `Favorites tied to the ${createdObjectMetadata.labelSingular}`,
+ icon: 'IconHeart',
+ isNullable: true,
+ },
+ // TO
+ {
+ objectMetadataId: favoriteObjectMetadata.id,
+ workspaceId: workspaceId,
+ isCustom: true,
+ isActive: true,
+ type: FieldMetadataType.RELATION,
+ name: createdObjectMetadata.nameSingular,
+ label: createdObjectMetadata.labelSingular,
+ targetColumnMap: {},
+ description: `Favorite ${createdObjectMetadata.labelSingular}`,
+ icon: 'IconBuildingSkyscraper',
+ isNullable: true,
+ },
+ // Foreign key
+ {
+ objectMetadataId: favoriteObjectMetadata.id,
+ workspaceId: workspaceId,
+ isCustom: true,
+ isActive: true,
+ type: FieldMetadataType.UUID,
+ name: `${createdObjectMetadata.nameSingular}Id`,
+ label: `${createdObjectMetadata.labelSingular} ID (foreign key)`,
+ targetColumnMap: {
+ value: `${createdObjectMetadata.targetTableName}Id`,
+ },
+ description: `Favorite ${createdObjectMetadata.labelSingular} id foreign key`,
+ icon: undefined,
+ isNullable: true,
+ isSystem: true,
+ defaultValue: undefined,
+ },
+ ]);
+
+ const favoriteRelationFieldMetadataMap =
+ favoriteRelationFieldMetadata.reduce(
+ (acc, fieldMetadata: FieldMetadataEntity) => {
+ if (fieldMetadata.type === FieldMetadataType.RELATION) {
+ acc[fieldMetadata.objectMetadataId] = fieldMetadata;
+ }
+
+ return acc;
+ },
+ {},
+ );
+
+ await this.relationMetadataRepository.save([
+ {
+ workspaceId: workspaceId,
+ relationType: RelationMetadataType.ONE_TO_MANY,
+ fromObjectMetadataId: createdObjectMetadata.id,
+ toObjectMetadataId: favoriteObjectMetadata.id,
+ fromFieldMetadataId:
+ favoriteRelationFieldMetadataMap[createdObjectMetadata.id].id,
+ toFieldMetadataId:
+ favoriteRelationFieldMetadataMap[favoriteObjectMetadata.id].id,
+ },
+ ]);
+
+ return { favoriteObjectMetadata };
+ }
}
diff --git a/packages/twenty-website/src/content/user-guide/basics/opportunities.mdx b/packages/twenty-website/src/content/user-guide/basics/opportunities.mdx
index 4625bf168..4c4ecab2c 100644
--- a/packages/twenty-website/src/content/user-guide/basics/opportunities.mdx
+++ b/packages/twenty-website/src/content/user-guide/basics/opportunities.mdx
@@ -10,7 +10,7 @@ All opportunities are presented in a Kanban board, where each column represents
## Add and delete stages
-You can add as many stages as you'd like to perfectly capture your entire workflow. To add a new stage, click on Options on the top right, choose Stages, and then click on `+ Add stage`. Name your stage and hit `Enter` to create it. Click on the name of the stage and then on on edit to change the stage's name and color.
+You can add as many stages as you'd like to perfectly capture your entire workflow. To add a new stage, click on Options on the top right, choose Stages, and then click on `+ Add stage`. Name your stage and hit `Enter` to create it. Click on the name of the stage and then on edit to change the stage's name and color.
To delete a stage, click on the stage name or on the `⋮` icon that appears when you hover over a stage. Click on edit to open the menu and click on Delete at the bottom to permanently delete a stage.