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:
@ -67,8 +67,8 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
|
|||||||
const showDate = beautifiedCreatedAt !== '';
|
const showDate = beautifiedCreatedAt !== '';
|
||||||
|
|
||||||
const author = comment.author;
|
const author = comment.author;
|
||||||
const authorName = author.name.firstName + ' ' + author.name.lastName;
|
const authorName = author?.name?.firstName + ' ' + author?.name?.lastName;
|
||||||
const avatarUrl = author.avatarUrl;
|
const avatarUrl = author?.avatarUrl;
|
||||||
const commentId = comment.id;
|
const commentId = comment.id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -77,7 +77,7 @@ export const CommentHeader = ({ comment, actionBar }: CommentHeaderProps) => {
|
|||||||
<Avatar
|
<Avatar
|
||||||
avatarUrl={avatarUrl}
|
avatarUrl={avatarUrl}
|
||||||
size="md"
|
size="md"
|
||||||
colorId={author.id}
|
colorId={author?.id}
|
||||||
placeholder={authorName}
|
placeholder={authorName}
|
||||||
/>
|
/>
|
||||||
<StyledName>{authorName}</StyledName>
|
<StyledName>{authorName}</StyledName>
|
||||||
|
|||||||
@ -86,6 +86,7 @@ export const ActivityComments = ({
|
|||||||
createOneComment?.({
|
createOneComment?.({
|
||||||
id: v4(),
|
id: v4(),
|
||||||
authorId: currentWorkspaceMember?.id ?? '',
|
authorId: currentWorkspaceMember?.id ?? '',
|
||||||
|
author: currentWorkspaceMember,
|
||||||
activityId: activity?.id ?? '',
|
activityId: activity?.id ?? '',
|
||||||
body: commentText,
|
body: commentText,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
|
|||||||
@ -20,6 +20,7 @@ export const useActivityTargets = ({
|
|||||||
eq: targetableObject.id,
|
eq: targetableObject.id,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
skip: !targetableObject.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -24,7 +24,6 @@ export const useTasks = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const isTargettingObjectRecords = isNonEmptyArray(targetableObjects);
|
const isTargettingObjectRecords = isNonEmptyArray(targetableObjects);
|
||||||
|
|
||||||
const targetableObjectsFilter =
|
const targetableObjectsFilter =
|
||||||
targetableObjects.reduce<LeafObjectRecordFilter>(
|
targetableObjects.reduce<LeafObjectRecordFilter>(
|
||||||
(aggregateFilter, targetableObject) => {
|
(aggregateFilter, targetableObject) => {
|
||||||
|
|||||||
@ -1,15 +1,7 @@
|
|||||||
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
|
|
||||||
|
|
||||||
export const getActivityTargetObjectFieldIdName = ({
|
export const getActivityTargetObjectFieldIdName = ({
|
||||||
nameSingular,
|
nameSingular,
|
||||||
}: {
|
}: {
|
||||||
nameSingular: string;
|
nameSingular: string;
|
||||||
}) => {
|
}) => {
|
||||||
const isCoreObject =
|
return `${nameSingular}Id`;
|
||||||
nameSingular === CoreObjectNameSingular.Company ||
|
|
||||||
nameSingular === CoreObjectNameSingular.Person;
|
|
||||||
|
|
||||||
const objectFieldIdName = `${!isCoreObject ? '_' : ''}${nameSingular}Id`;
|
|
||||||
|
|
||||||
return objectFieldIdName;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -91,12 +91,12 @@ export const useFavorites = () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const createFavorite = (
|
const createFavorite = (
|
||||||
targetObject: Record<string, any>,
|
targetRecord: Record<string, any>,
|
||||||
targetObjectNameSingular: string,
|
targetObjectNameSingular: string,
|
||||||
) => {
|
) => {
|
||||||
createOneFavorite({
|
createOneFavorite({
|
||||||
[`${targetObjectNameSingular}Id`]: targetObject.id,
|
[`${targetObjectNameSingular}Id`]: targetRecord.id,
|
||||||
[`${targetObjectNameSingular}`]: targetObject,
|
[`${targetObjectNameSingular}`]: targetRecord,
|
||||||
position: favorites.length + 1,
|
position: favorites.length + 1,
|
||||||
workspaceMemberId: currentWorkspaceMember?.id,
|
workspaceMemberId: currentWorkspaceMember?.id,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -26,12 +26,12 @@ export const seedFeatureFlags = async (
|
|||||||
{
|
{
|
||||||
key: 'IS_RATING_FIELD_TYPE_ENABLED',
|
key: 'IS_RATING_FIELD_TYPE_ENABLED',
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
value: true,
|
value: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'IS_RELATION_FIELD_CARD_ENABLED',
|
key: 'IS_RELATION_FIELD_CARD_ENABLED',
|
||||||
workspaceId: workspaceId,
|
workspaceId: workspaceId,
|
||||||
value: true,
|
value: false,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
@ -141,88 +141,16 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const activityTargetObjectMetadata =
|
const { activityTargetObjectMetadata } =
|
||||||
await this.objectMetadataRepository.findOneByOrFail({
|
await this.createActivityTargetRelation(
|
||||||
nameSingular: 'activityTarget',
|
objectMetadataInput.workspaceId,
|
||||||
workspaceId: objectMetadataInput.workspaceId,
|
createdObjectMetadata,
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
},
|
|
||||||
{},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.relationMetadataRepository.save([
|
const { favoriteObjectMetadata } = await this.createFavoriteRelation(
|
||||||
{
|
objectMetadataInput.workspaceId,
|
||||||
workspaceId: objectMetadataInput.workspaceId,
|
createdObjectMetadata,
|
||||||
relationType: RelationMetadataType.ONE_TO_MANY,
|
);
|
||||||
fromObjectMetadataId: createdObjectMetadata.id,
|
|
||||||
toObjectMetadataId: activityTargetObjectMetadata.id,
|
|
||||||
fromFieldMetadataId:
|
|
||||||
activityTargetRelationFieldMetadataMap[createdObjectMetadata.id].id,
|
|
||||||
toFieldMetadataId:
|
|
||||||
activityTargetRelationFieldMetadataMap[
|
|
||||||
activityTargetObjectMetadata.id
|
|
||||||
].id,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
await this.workspaceMigrationService.createCustomMigration(
|
await this.workspaceMigrationService.createCustomMigration(
|
||||||
createdObjectMetadata.workspaceId,
|
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
|
// This is temporary until we implement mainIdentifier
|
||||||
{
|
{
|
||||||
name: createdObjectMetadata.targetTableName,
|
name: createdObjectMetadata.targetTableName,
|
||||||
@ -365,4 +318,182 @@ export class ObjectMetadataService extends TypeOrmQueryService<ObjectMetadataEnt
|
|||||||
public async deleteObjectsMetadata(workspaceId: string) {
|
public async deleteObjectsMetadata(workspaceId: string) {
|
||||||
await this.objectMetadataRepository.delete({ workspaceId });
|
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 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ All opportunities are presented in a Kanban board, where each column represents
|
|||||||
|
|
||||||
## Add and delete stages
|
## 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.
|
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.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user