Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Tag Type #8692

Merged
merged 5 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions packages/contracts/src/lib/tag.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IRelationalOrganizationTeam } from './organization-team.model';
import { IBasePerTenantAndOrganizationEntityModel } from './base-entity.model';
import { IBasePerTenantAndOrganizationEntityModel, ID } from './base-entity.model';

export interface ITaggable {
tags?: ITag[];
Expand All @@ -12,6 +12,8 @@ export interface ITag extends IBasePerTenantAndOrganizationEntityModel, IRelatio
icon?: string;
description?: string;
isSystem?: boolean;
tagTypeId?: ID;
tagType?: ITagType;
}

export interface ITagFindInput extends IBasePerTenantAndOrganizationEntityModel, Pick<ITag, 'organizationTeamId'> {
Expand All @@ -20,12 +22,18 @@ export interface ITagFindInput extends IBasePerTenantAndOrganizationEntityModel,
textColor?: string;
description?: string;
isSystem?: boolean;
tagTypeId?: ID;
}

export interface ITagCreateInput extends ITag {}

export interface ITagType {
type: string;
tags?: ITag[];
}

export interface ITagUpdateInput extends Partial<ITagCreateInput> {
id?: string;
id?: ID;
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,4 @@ export {
ExpenseCategoryFirstOrCreateCommand
} from './lib/expense-categories';
export { TagModule, TagService, Taggable, AutomationLabelSyncCommand, RelationalTagDTO } from './lib/tags';
export { TagTypeModule, TagTypeService } from './lib/tag-type';
2 changes: 2 additions & 0 deletions packages/core/src/lib/core/entities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ import {
SocialAccount,
Subscription,
Tag,
TagType,
Task,
TaskEstimation,
TaskLinkedIssue,
Expand Down Expand Up @@ -286,6 +287,7 @@ export const coreEntities = [
SocialAccount,
Subscription,
Tag,
TagType,
Task,
TaskEstimation,
TaskLinkedIssue,
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/lib/core/entities/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export * from '../../role/role.entity';
export * from '../../skills/skill.entity';
export * from '../../subscription/subscription.entity';
export * from '../../tags/tag.entity';
export * from '../../tag-type/tag-type.entity';
export * from '../../tasks/daily-plan/daily-plan.entity';
export * from '../../tasks/estimation/task-estimation.entity';
export * from '../../tasks/issue-type/issue-type.entity';
Expand Down
35 changes: 35 additions & 0 deletions packages/core/src/lib/tag-type/default-tag-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ITagType } from '@gauzy/contracts';

/**
* Represents the default tag types used throughout the application.
* Each tag type corresponds to a specific category or grouping.
*
* @constant DEFAULT_TAG_TYPES
* @type {ITagType[]}
*
* @description
* This constant defines an array of objects that represent default tag types.
* Each object contains a `type` property, which is a string describing the tag type.
* These tags can be used to classify various entities such as equipment, income,
* invoices, and more.
*
* Example Usage:
* ```typescript
* console.log(DEFAULT_TAG_TYPES);
* ```
*
*/
export const DEFAULT_TAG_TYPES: ITagType[] = [
{ type: 'Equipment' },
{ type: 'Income' },
{ type: 'Invoice' },
{ type: 'Payment' },
{ type: 'Task' },
{ type: 'Proposals' },
{ type: 'Organization Contact' },
{ type: 'Employee Level' },
{ type: 'Organization Department' },
{ type: 'Warehouse' },
{ type: 'Employee' },
{ type: 'User' }
];
2 changes: 2 additions & 0 deletions packages/core/src/lib/tag-type/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './tag-type.module';
export * from './tag-type.service';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { MikroOrmBaseEntityRepository } from '../../core/repository/mikro-orm-base-entity.repository';
import { TagType } from '../tag-type.entity';

export class MikroOrmTagTypeRepository extends MikroOrmBaseEntityRepository<TagType> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TagType } from '../tag-type.entity';

@Injectable()
export class TypeOrmTagTypeRepository extends Repository<TagType> {
constructor(@InjectRepository(TagType) readonly repository: Repository<TagType>) {
super(repository.target, repository.manager, repository.queryRunner);
}
}
61 changes: 61 additions & 0 deletions packages/core/src/lib/tag-type/tag-type.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { ApiTags, ApiResponse, ApiOperation } from '@nestjs/swagger';
import { Controller, HttpStatus, Get, Query, UseGuards, ValidationPipe } from '@nestjs/common';
import { FindOptionsWhere } from 'typeorm';
import { IPagination } from '@gauzy/contracts';
import { CrudController, PaginationParams } from '../core/crud';
import { PermissionGuard, TenantPermissionGuard } from '../shared/guards';
import { TagType } from './tag-type.entity';
import { TagTypeService } from './tag-type.service';

@ApiTags('TagTypes')
@UseGuards(TenantPermissionGuard, PermissionGuard)
@Controller('/tag-types')
export class TagTypeController extends CrudController<TagType> {
constructor(private readonly tagTypesService: TagTypeService) {
super(tagTypesService);
}

/**
* GET tag types count
*
* @param data
* @returns
*/
@ApiOperation({ summary: 'Find Tag Types Count ' })
@ApiResponse({
status: HttpStatus.OK,
description: 'Count Tag Types',
type: TagType
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Record not found'
})
@Get('count')
async getCount(@Query() options: FindOptionsWhere<TagType>): Promise<number> {
return await this.tagTypesService.countBy(options);
}

/**
* GET all tag types
*
* @param options
* @returns
*/
@ApiOperation({
summary: 'Find all tag types.'
})
@ApiResponse({
status: HttpStatus.OK,
description: 'Found tag types.',
type: TagType
})
@ApiResponse({
status: HttpStatus.NOT_FOUND,
description: 'Record not found'
})
@Get()
async findAll(@Query(new ValidationPipe()) options: PaginationParams<TagType>): Promise<IPagination<TagType>> {
return await this.tagTypesService.findAll(options);
}
}
27 changes: 27 additions & 0 deletions packages/core/src/lib/tag-type/tag-type.entity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { ApiProperty } from '@nestjs/swagger';
import { ITag, ITagType } from '@gauzy/contracts';
import { IsNotEmpty, IsString } from 'class-validator';
import { Tag, TenantOrganizationBaseEntity } from '../core/entities/internal';
import { MultiORMColumn, MultiORMEntity, MultiORMOneToMany } from './../core/decorators/entity';
import { MikroOrmTagTypeRepository } from './repository/mikro-orm-tag-type.repository';

@MultiORMEntity('tag_type', { mikroOrmRepository: () => MikroOrmTagTypeRepository })
export class TagType extends TenantOrganizationBaseEntity implements ITagType {
@ApiProperty({ type: () => String })
@IsNotEmpty()
@IsString()
@MultiORMColumn()
type: string;

/*
|--------------------------------------------------------------------------
| @OneToMany
|--------------------------------------------------------------------------
*/
/**
* tags
*/
@ApiProperty({ type: () => Tag, isArray: true })
@MultiORMOneToMany(() => Tag, (tag) => tag.tagType)
tags?: ITag[];
}
22 changes: 22 additions & 0 deletions packages/core/src/lib/tag-type/tag-type.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MikroOrmModule } from '@mikro-orm/nestjs';
import { RolePermissionModule } from '../role-permission/role-permission.module';
import { TagType } from './tag-type.entity';
import { TagTypeService } from './tag-type.service';
import { TagTypeController } from './tag-type.controller';
import { TypeOrmTagTypeRepository } from './repository/type-orm-tag-type.repository';

@Module({
imports: [
CqrsModule,
TypeOrmModule.forFeature([TagType]),
MikroOrmModule.forFeature([TagType]),
RolePermissionModule
],
controllers: [TagTypeController],
providers: [TagTypeService, TypeOrmTagTypeRepository],
rahul-rocket marked this conversation as resolved.
Show resolved Hide resolved
exports: [TagTypeService]
})
export class TagTypeModule {}
80 changes: 80 additions & 0 deletions packages/core/src/lib/tag-type/tag-type.seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { DataSource } from 'typeorm';
import { IOrganization, ITagType, ITenant } from '@gauzy/contracts';
import { TagType } from './tag-type.entity';
import { DEFAULT_TAG_TYPES } from './default-tag-types';

/**
* Creates and inserts tag types into the database for a specified tenant and organizations.
*
* @function createTagTypes
* @async
* @param {DataSource} dataSource - The TypeORM `DataSource` instance used for database operations.
* @param {ITenant} tenant - The tenant for which the tag types are being created.
* @param {IOrganization[]} organizations - An array of organizations associated with the tag types.
* @returns {Promise<ITagType[]>} - A promise that resolves to the array of created and inserted `ITagType` entities.
*
* @description
* This function iterates over the predefined `DEFAULT_TAG_TYPES` and creates `TagType` entities
* for each organization provided. It assigns the `type` from `DEFAULT_TAG_TYPES`, associates the
* organization and tenant, and saves the resulting entities into the database.
*
* @example
* const organizations = [
* { id: 'org1', name: 'Org 1' },
* { id: 'org2', name: 'Org 2' },
* ];
*
* const tenant = { id: 'tenant1', name: 'Tenant 1' };
*
* const createdTags = await createTagTypes(dataSource, tenant, organizations);
* console.log(createdTags);
*
* @throws Will throw an error if the database save operation fails.
*/
export const createTagTypes = async (
dataSource: DataSource,
tenant: ITenant,
organizations: IOrganization[]
): Promise<ITagType[]> => {
// Array to store the new tag type entities
const tagTypes: TagType[] = [];

// Iterate over the predefined default tag types
DEFAULT_TAG_TYPES.forEach(({ type }) => {
// Create a tag type for each organization
for (const organization of organizations) {
const entity = new TagType();
entity.type = type; // Assign type from default list
entity.organization = organization; // Associate organization
entity.tenant = tenant; // Associate tenant
tagTypes.push(entity);
}
});

// Save the created tag types into the database
return insertTagTypes(dataSource, tagTypes);
};
rahul-rocket marked this conversation as resolved.
Show resolved Hide resolved

/**
* Inserts an array of tag types into the database.
*
* @function insertTagTypes
* @async
* @param {DataSource} dataSource - The TypeORM `DataSource` instance used to interact with the database.
* @param {TagType[]} tagTypes - An array of `TagType` entities to be saved into the database.
* @returns {Promise<TagType[]>} - A promise that resolves with the array of saved `TagType` entities.
*
* @example
* // Example usage:
* const tagTypes = [
* { type: 'Equipment' },
* { type: 'Income' },
* ];
*
* await insertTagTypes(dataSource, tagTypes);
*
* @throws Will throw an error if the database save operation fails.
*/
const insertTagTypes = async (dataSource: DataSource, tagTypes: TagType[]): Promise<TagType[]> => {
return await dataSource.manager.save(tagTypes);
};
rahul-rocket marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions packages/core/src/lib/tag-type/tag-type.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Injectable } from '@nestjs/common';
import { TenantAwareCrudService } from '../core/crud';
import { TagType } from './tag-type.entity';
import { MikroOrmTagTypeRepository } from './repository/mikro-orm-tag-type.repository';
import { TypeOrmTagTypeRepository } from './repository/type-orm-tag-type.repository';

@Injectable()
export class TagTypeService extends TenantAwareCrudService<TagType> {
constructor(
typeOrmTagTypeRepository: TypeOrmTagTypeRepository,
mikroOrmTagTypeRepository: MikroOrmTagTypeRepository
) {
super(typeOrmTagTypeRepository, mikroOrmTagTypeRepository);
}
}
24 changes: 22 additions & 2 deletions packages/core/src/lib/tags/tag.entity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { RelationId } from 'typeorm';
import { JoinColumn, RelationId } from 'typeorm';
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { EntityRepositoryType } from '@mikro-orm/core';
import { IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
import {
ICandidate,
ID,
IEmployee,
IEmployeeLevel,
IEquipment,
Expand All @@ -27,6 +28,7 @@ import {
IProduct,
IRequestApproval,
ITag,
ITagType,
ITask,
IUser,
IWarehouse
Expand Down Expand Up @@ -54,6 +56,7 @@ import {
Payment,
Product,
RequestApproval,
TagType,
Task,
TenantOrganizationBaseEntity,
User,
Expand Down Expand Up @@ -115,6 +118,23 @@ export class Tag extends TenantOrganizationBaseEntity implements ITag {
@VirtualMultiOrmColumn()
fullIconUrl?: string;

/**
* TagType
*/
@ApiProperty({ type: () => TagType })
@MultiORMManyToOne(() => TagType, (it) => it.tags, {
onDelete: 'SET NULL'
})
@JoinColumn()
tagType?: ITagType;

@ApiProperty({ type: () => String })
@RelationId((it: Tag) => it.tagType)
@IsString()
@ColumnIndex()
@MultiORMColumn({ nullable: true, relationId: true })
tagTypeId?: ID;

/*
|--------------------------------------------------------------------------
| @ManyToOne
Expand All @@ -139,7 +159,7 @@ export class Tag extends TenantOrganizationBaseEntity implements ITag {
@RelationId((it: Tag) => it.organizationTeam)
@ColumnIndex()
@MultiORMColumn({ nullable: true, relationId: true })
organizationTeamId?: IOrganizationTeam['id'];
organizationTeamId?: ID;

/*
|--------------------------------------------------------------------------
Expand Down
Loading