Fix relation creation bug + enable favorite for custom objects (#3392)

* Fix relation creation bug

* Fix vale CI

* Fix comment bug
This commit is contained in:
Charles Bochet
2024-01-11 22:46:43 +01:00
committed by GitHub
parent 3ad032cdc1
commit 10fd67ba32
9 changed files with 223 additions and 99 deletions

View File

@ -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) => {
<Avatar
avatarUrl={avatarUrl}
size="md"
colorId={author.id}
colorId={author?.id}
placeholder={authorName}
/>
<StyledName>{authorName}</StyledName>

View File

@ -86,6 +86,7 @@ export const ActivityComments = ({
createOneComment?.({
id: v4(),
authorId: currentWorkspaceMember?.id ?? '',
author: currentWorkspaceMember,
activityId: activity?.id ?? '',
body: commentText,
createdAt: new Date().toISOString(),

View File

@ -20,6 +20,7 @@ export const useActivityTargets = ({
eq: targetableObject.id,
},
},
skip: !targetableObject.id,
});
return {

View File

@ -24,7 +24,6 @@ export const useTasks = ({
});
const isTargettingObjectRecords = isNonEmptyArray(targetableObjects);
const targetableObjectsFilter =
targetableObjects.reduce<LeafObjectRecordFilter>(
(aggregateFilter, targetableObject) => {

View File

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

View File

@ -91,12 +91,12 @@ export const useFavorites = () => {
]);
const createFavorite = (
targetObject: Record<string, any>,
targetRecord: Record<string, any>,
targetObjectNameSingular: string,
) => {
createOneFavorite({
[`${targetObjectNameSingular}Id`]: targetObject.id,
[`${targetObjectNameSingular}`]: targetObject,
[`${targetObjectNameSingular}Id`]: targetRecord.id,
[`${targetObjectNameSingular}`]: targetRecord,
position: favorites.length + 1,
workspaceMemberId: currentWorkspaceMember?.id,
});

View File

@ -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();

View File

@ -141,88 +141,16 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
],
});
const activityTargetObjectMetadata =
await this.objectMetadataRepository.findOneByOrFail({
nameSingular: 'activityTarget',
workspaceId: objectMetadataInput.workspaceId,
});
const activityTargetRelationFieldMetadata =
await this.fieldMetadataRepository.save([
// FROM
{
objectMetadataId: createdObjectMetadata.id,
workspaceId: objectMetadataInput.workspaceId,
isCustom: true,
isActive: true,
type: FieldMetadataType.RELATION,
name: 'activityTargets',
label: 'Activities',
targetColumnMap: {},
description: `Activities tied to the ${objectMetadataInput.labelSingular}`,
icon: 'IconCheckbox',
isNullable: true,
},
// TO
{
objectMetadataId: activityTargetObjectMetadata.id,
workspaceId: objectMetadataInput.workspaceId,
isCustom: true,
isActive: true,
type: FieldMetadataType.RELATION,
name: objectMetadataInput.nameSingular,
label: objectMetadataInput.labelSingular,
targetColumnMap: {
value: `${createdObjectMetadata.targetTableName}Id`,
},
description: `ActivityTarget ${objectMetadataInput.labelSingular}`,
icon: 'IconBuildingSkyscraper',
isNullable: true,
},
// Foreign key
{
objectMetadataId: activityTargetObjectMetadata.id,
workspaceId: objectMetadataInput.workspaceId,
isCustom: true,
isActive: true,
type: FieldMetadataType.UUID,
name: `${createdObjectMetadata.targetTableName}Id`,
label: `${objectMetadataInput.labelSingular} ID (foreign key)`,
targetColumnMap: {},
description: `ActivityTarget ${objectMetadataInput.labelSingular} id foreign key`,
icon: undefined,
isNullable: true,
isSystem: true,
defaultValue: undefined,
},
]);
const activityTargetRelationFieldMetadataMap =
activityTargetRelationFieldMetadata.reduce(
(acc, fieldMetadata: FieldMetadataEntity) => {
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<ObjectMetadataEnt
},
],
},
// Add favorite relation
{
name: favoriteObjectMetadata.targetTableName,
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.CREATE,
columnName: `${createdObjectMetadata.targetTableName}Id`,
columnType: 'uuid',
isNullable: true,
} satisfies WorkspaceMigrationColumnCreate,
],
},
{
name: favoriteObjectMetadata.targetTableName,
action: 'alter',
columns: [
{
action: WorkspaceMigrationColumnActionType.RELATION,
columnName: `${createdObjectMetadata.targetTableName}Id`,
referencedTableName: createdObjectMetadata.targetTableName,
referencedTableColumnName: 'id',
},
],
},
// This is temporary until we implement mainIdentifier
{
name: createdObjectMetadata.targetTableName,
@ -365,4 +318,182 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
public async deleteObjectsMetadata(workspaceId: string) {
await this.objectMetadataRepository.delete({ workspaceId });
}
private async createActivityTargetRelation(
workspaceId: string,
createdObjectMetadata: ObjectMetadataEntity,
) {
const activityTargetObjectMetadata =
await this.objectMetadataRepository.findOneByOrFail({
nameSingular: 'activityTarget',
workspaceId: workspaceId,
});
const activityTargetRelationFieldMetadata =
await this.fieldMetadataRepository.save([
// FROM
{
objectMetadataId: createdObjectMetadata.id,
workspaceId: workspaceId,
isCustom: true,
isActive: true,
type: FieldMetadataType.RELATION,
name: 'activityTargets',
label: 'Activities',
targetColumnMap: {},
description: `Activities tied to the ${createdObjectMetadata.labelSingular}`,
icon: 'IconCheckbox',
isNullable: true,
},
// TO
{
objectMetadataId: activityTargetObjectMetadata.id,
workspaceId: workspaceId,
isCustom: true,
isActive: true,
type: FieldMetadataType.RELATION,
name: createdObjectMetadata.nameSingular,
label: createdObjectMetadata.labelSingular,
targetColumnMap: {},
description: `ActivityTarget ${createdObjectMetadata.labelSingular}`,
icon: 'IconBuildingSkyscraper',
isNullable: true,
},
// Foreign key
{
objectMetadataId: activityTargetObjectMetadata.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: `ActivityTarget ${createdObjectMetadata.labelSingular} id foreign key`,
icon: undefined,
isNullable: true,
isSystem: true,
defaultValue: undefined,
},
]);
const activityTargetRelationFieldMetadataMap =
activityTargetRelationFieldMetadata.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: 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 };
}
}

View File

@ -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 <b>Options</b> on the top right, choose <b>Stages</b>, 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 <b>Options</b> on the top right, choose <b>Stages</b>, 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 <b>Delete</b> at the bottom to permanently delete a stage.