From be6e4a8d329980652c3f3cdff571f45bb8acf1cf Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Sun, 26 Jul 2026 08:44:56 +0330 Subject: [PATCH] ticket master --- .../1785000000000-addTicketMasterEntity.ts | 19 ++ ...85100000000-addTicketMastersPermissions.ts | 179 ++++++++++++++++++ src/common/enums/message.enum.ts | 6 + .../tickets/DTO/create-ticket-master.dto.ts | 11 ++ .../tickets/DTO/update-ticket-master.dto.ts | 11 ++ .../tickets/entities/ticket-master.entity.ts | 12 ++ .../tickets/providers/tickets.service.ts | 94 ++++++++- .../repositories/ticket-master.repository.ts | 33 ++++ src/modules/tickets/tickets.controller.ts | 38 +++- src/modules/tickets/tickets.module.ts | 5 +- .../users/constants/permission-meta.ts | 5 + src/modules/users/enums/permission.enum.ts | 6 + src/modules/users/providers/admins.service.ts | 4 + src/modules/users/users.controller.ts | 8 +- 14 files changed, 427 insertions(+), 4 deletions(-) create mode 100644 database/migrations/1785000000000-addTicketMasterEntity.ts create mode 100644 database/migrations/1785100000000-addTicketMastersPermissions.ts create mode 100644 src/modules/tickets/DTO/create-ticket-master.dto.ts create mode 100644 src/modules/tickets/DTO/update-ticket-master.dto.ts create mode 100644 src/modules/tickets/entities/ticket-master.entity.ts create mode 100644 src/modules/tickets/repositories/ticket-master.repository.ts diff --git a/database/migrations/1785000000000-addTicketMasterEntity.ts b/database/migrations/1785000000000-addTicketMasterEntity.ts new file mode 100644 index 0000000..8bc848a --- /dev/null +++ b/database/migrations/1785000000000-addTicketMasterEntity.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class AddTicketMasterEntity1785000000000 implements MigrationInterface { + name = "AddTicketMasterEntity1785000000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "ticket_master" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "userId" uuid NOT NULL, CONSTRAINT "UQ_ticket_master_user" UNIQUE ("userId"), CONSTRAINT "PK_ticket_master" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "ticket_master" ADD CONSTRAINT "FK_ticket_master_user" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "ticket_master" DROP CONSTRAINT "FK_ticket_master_user"`); + await queryRunner.query(`DROP TABLE "ticket_master"`); + } +} diff --git a/database/migrations/1785100000000-addTicketMastersPermissions.ts b/database/migrations/1785100000000-addTicketMastersPermissions.ts new file mode 100644 index 0000000..4505f64 --- /dev/null +++ b/database/migrations/1785100000000-addTicketMastersPermissions.ts @@ -0,0 +1,179 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +const NEW_PERMISSIONS = [ + { name: "ticket_masters_create", title: "ایجاد مستر تیکت", source: "tickets_create" }, + { name: "ticket_masters_read", title: "مشاهده مستر تیکت", source: "tickets_read" }, + { name: "ticket_masters_update", title: "ویرایش مستر تیکت", source: "tickets_update" }, + { name: "ticket_masters_delete", title: "حذف مستر تیکت", source: "tickets_delete" }, +] as const; + +const CURRENT_ENUM_VALUES = [ + "services_create", + "services_read", + "services_update", + "services_delete", + "customers_create", + "customers_read", + "customers_update", + "customers_delete", + "agents_create", + "agents_read", + "agents_update", + "agents_delete", + "developers_create", + "developers_read", + "developers_update", + "developers_delete", + "invoices_create", + "invoices_read", + "invoices_update", + "invoices_delete", + "transactions_create", + "transactions_read", + "transactions_update", + "transactions_delete", + "discounts_create", + "discounts_read", + "discounts_update", + "discounts_delete", + "admins_create", + "admins_read", + "admins_update", + "admins_delete", + "tickets_create", + "tickets_read", + "tickets_update", + "tickets_delete", + "ticket_categories_create", + "ticket_categories_read", + "ticket_categories_update", + "ticket_categories_delete", + "criticisms_create", + "criticisms_read", + "criticisms_update", + "criticisms_delete", + "contacts_us_create", + "contacts_us_read", + "contacts_us_update", + "contacts_us_delete", + "advertisements_create", + "advertisements_read", + "advertisements_update", + "advertisements_delete", + "announcements_create", + "announcements_read", + "announcements_update", + "announcements_delete", + "blogs_create", + "blogs_read", + "blogs_update", + "blogs_delete", + "learnings_create", + "learnings_read", + "learnings_update", + "learnings_delete", + "settings_create", + "settings_read", + "settings_update", + "settings_delete", + "bank_accounts_create", + "bank_accounts_read", + "bank_accounts_update", + "bank_accounts_delete", + "payments_create", + "payments_read", + "payments_update", + "payments_delete", + "support_plan_create", + "support_plan_read", + "support_plan_update", + "support_plan_delete", + "reseller_create", + "reseller_read", + "reseller_update", + "reseller_delete", + "workspace_create", + "workspace_read", + "workspace_update", + "workspace_delete", + "project_create", + "project_read", + "project_update", + "project_delete", + "task_create", + "task_read", + "task_update", + "task_delete", + "task_phase_create", + "task_phase_read", + "task_phase_update", + "task_phase_delete", + "logs", + "manage_sso_clients", + "dmenu", + "dkala", + "dpage", + "dmail", + "view_all_workspaces", + "view_all_projects", +]; + +const UPDATED_ENUM_VALUES = [ + ...CURRENT_ENUM_VALUES.slice(0, CURRENT_ENUM_VALUES.indexOf("ticket_categories_delete") + 1), + ...NEW_PERMISSIONS.map((p) => p.name), + ...CURRENT_ENUM_VALUES.slice(CURRENT_ENUM_VALUES.indexOf("ticket_categories_delete") + 1), +]; + +export class AddTicketMastersPermissions1785100000000 implements MigrationInterface { + name = "AddTicketMastersPermissions1785100000000"; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`); + + const enumList = UPDATED_ENUM_VALUES.map((v) => `'${v}'`).join(", "); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM(${enumList})`); + await queryRunner.query( + `ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"public"."permission_name_enum"`, + ); + + for (const perm of NEW_PERMISSIONS) { + const inserted = await queryRunner.query( + `INSERT INTO "permission" ("id", "name", "title", "group", "createdAt", "updatedAt") + VALUES (uuid_generate_v4(), $1, $2, 'support', NOW(), NOW()) + RETURNING id`, + [perm.name, perm.title], + ); + const newId = inserted[0].id as string; + + const sourcePerm = await queryRunner.query(`SELECT id FROM "permission" WHERE "name" = $1`, [perm.source]); + if (!sourcePerm.length) continue; + + await queryRunner.query( + `INSERT INTO "role_permission_relation" ("roleId", "permissionId") + SELECT "roleId", $1 FROM "role_permission_relation" WHERE "permissionId" = $2 + ON CONFLICT ("roleId", "permissionId") DO NOTHING`, + [newId, sourcePerm[0].id], + ); + } + } + + public async down(queryRunner: QueryRunner): Promise { + for (const perm of NEW_PERMISSIONS) { + const rows = await queryRunner.query(`SELECT id FROM "permission" WHERE "name" = $1`, [perm.name]); + if (!rows.length) continue; + + await queryRunner.query(`DELETE FROM "role_permission_relation" WHERE "permissionId" = $1`, [rows[0].id]); + await queryRunner.query(`DELETE FROM "permission" WHERE "id" = $1`, [rows[0].id]); + } + + await queryRunner.query(`ALTER TABLE "permission" ALTER COLUMN "name" TYPE text USING "name"::text`); + await queryRunner.query(`DROP TYPE "public"."permission_name_enum"`); + + const enumList = CURRENT_ENUM_VALUES.map((v) => `'${v}'`).join(", "); + await queryRunner.query(`CREATE TYPE "public"."permission_name_enum" AS ENUM(${enumList})`); + await queryRunner.query( + `ALTER TABLE "permission" ALTER COLUMN "name" TYPE "public"."permission_name_enum" USING "name"::"public"."permission_name_enum"`, + ); + } +} diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 2b54300..cda039e 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -330,6 +330,12 @@ export const enum TicketMessageEnum { INVALID_TICKET_LIMIT = "محدودیت تعداد تیکت در پلن پشتیبانی شما نامعتبر است", TICKET_LIMIT_EXCEEDED = "شما به محدودیت تعداد تیکت در پلن پشتیبانی خود رسیده‌اید", OPENED = "تیکت با موفقیت باز شد", + MASTER_USER_ID_REQUIRED = "شناسه کاربر مستر تیکت اجباری است", + MASTER_USER_ID_SHOULD_BE_UUID = "شناسه کاربر مستر تیکت باید یک UUID معتبر باشد", + MASTER_CREATED = "مستر تیکت با موفقیت ایجاد شد", + MASTER_NOT_FOUND = "مستر تیکت یافت نشد", + MASTER_EXIST = "این کاربر قبلاً به عنوان مستر تیکت ثبت شده است", + MASTER_DELETED = "مستر تیکت با موفقیت حذف شد", } export const enum WalletMessage { diff --git a/src/modules/tickets/DTO/create-ticket-master.dto.ts b/src/modules/tickets/DTO/create-ticket-master.dto.ts new file mode 100644 index 0000000..df728bd --- /dev/null +++ b/src/modules/tickets/DTO/create-ticket-master.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsUUID } from "class-validator"; + +import { TicketMessageEnum } from "../../../common/enums/message.enum"; + +export class CreateTicketMasterDto { + @IsNotEmpty({ message: TicketMessageEnum.MASTER_USER_ID_REQUIRED }) + @IsUUID("4", { message: TicketMessageEnum.MASTER_USER_ID_SHOULD_BE_UUID }) + @ApiProperty({ description: "User ID of the ticket master", example: "123e4567-e89b-12d3-a456-426614174000" }) + userId: string; +} diff --git a/src/modules/tickets/DTO/update-ticket-master.dto.ts b/src/modules/tickets/DTO/update-ticket-master.dto.ts new file mode 100644 index 0000000..f3ec117 --- /dev/null +++ b/src/modules/tickets/DTO/update-ticket-master.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsUUID } from "class-validator"; + +import { TicketMessageEnum } from "../../../common/enums/message.enum"; + +export class UpdateTicketMasterDto { + @IsNotEmpty({ message: TicketMessageEnum.MASTER_USER_ID_REQUIRED }) + @IsUUID("4", { message: TicketMessageEnum.MASTER_USER_ID_SHOULD_BE_UUID }) + @ApiProperty({ description: "User ID of the ticket master", example: "123e4567-e89b-12d3-a456-426614174000" }) + userId: string; +} diff --git a/src/modules/tickets/entities/ticket-master.entity.ts b/src/modules/tickets/entities/ticket-master.entity.ts new file mode 100644 index 0000000..cd25d99 --- /dev/null +++ b/src/modules/tickets/entities/ticket-master.entity.ts @@ -0,0 +1,12 @@ +import { Entity, JoinColumn, ManyToOne, Unique } from "typeorm"; + +import { BaseEntity } from "../../../common/entities/base.entity"; +import { User } from "../../users/entities/user.entity"; + +@Entity("ticket_master") +@Unique(["user"]) +export class TicketMaster extends BaseEntity { + @ManyToOne(() => User, { nullable: false, onDelete: "CASCADE" }) + @JoinColumn() + user: User; +} diff --git a/src/modules/tickets/providers/tickets.service.ts b/src/modules/tickets/providers/tickets.service.ts index 0d4b9ed..05ce7f2 100755 --- a/src/modules/tickets/providers/tickets.service.ts +++ b/src/modules/tickets/providers/tickets.service.ts @@ -19,6 +19,7 @@ import { User } from "../../users/entities/user.entity"; import { AdminsService } from "../../users/providers/admins.service"; import { UsersService } from "../../users/providers/users.service"; import { CreateTicketCategoryDto } from "../DTO/create-ticket-category.dto"; +import { CreateTicketMasterDto } from "../DTO/create-ticket-master.dto"; import { CreateTicketMessageDto } from "../DTO/create-ticket-message.dto"; import { CreateTicketByAdminDto, CreateTicketDto } from "../DTO/create-ticket.dto"; import { ReferTicketDto } from "../DTO/refer-ticket.dto"; @@ -30,6 +31,7 @@ import { TicketMessageAttachment } from "../entities/ticket-message-attachment"; import { TicketMessage } from "../entities/ticket-message.entity"; import { Ticket } from "../entities/ticket.entity"; import { TicketStatus } from "../enums/ticket-status.enum"; +import { TicketMasterRepository } from "../repositories/ticket-master.repository"; import { TicketCategoryRepository } from "../repositories/tickets-category.repository"; import { TicketMessagesRepository } from "../repositories/tickets-message.repository"; import { TicketsRepository } from "../repositories/tickets.repository"; @@ -43,6 +45,7 @@ export class TicketsService implements OnModuleInit { @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue, private readonly ticketsRepository: TicketsRepository, private readonly ticketsCategoryRepository: TicketCategoryRepository, + private readonly ticketMasterRepository: TicketMasterRepository, private readonly ticketMessagesRepository: TicketMessagesRepository, private readonly usersService: UsersService, private readonly adminsService: AdminsService, @@ -167,6 +170,79 @@ export class TicketsService implements OnModuleInit { }; } + //******************************** */ + // ticket masters + //******************************** */ + + async createTicketMaster(createDto: CreateTicketMasterDto, userIpAndHeaders: IUserIpAndHeaders) { + const { user } = await this.usersService.findOneById(createDto.userId); + + const existingMaster = await this.ticketMasterRepository.findByUserId(createDto.userId); + if (existingMaster) throw new BadRequestException(TicketMessageEnum.MASTER_EXIST); + + const ticketMaster = this.ticketMasterRepository.create({ user }); + await this.ticketMasterRepository.save(ticketMaster); + + await this.accessLogService.logCreate("TicketMaster", UserType.ADMIN, { + user: { id: userIpAndHeaders.userId }, + endpoint: "/tickets/masters", + requestId: userIpAndHeaders.headers["x-request-id"], + ipAddress: userIpAndHeaders.ip, + userAgent: userIpAndHeaders.headers["user-agent"], + actionDescription: `Admin created ticket master for user: ${user.id}`, + metadata: { + ticketMasterId: ticketMaster.id, + masterUserId: user.id, + }, + }); + + return { + message: TicketMessageEnum.MASTER_CREATED, + ticketMaster: await this.ticketMasterRepository.findByIdWithUser(ticketMaster.id), + }; + } + + //******************************** */ + + async getTicketMasters() { + const ticketMasters = await this.ticketMasterRepository.findAllWithUser(); + return { ticketMasters }; + } + + //******************************** */ + + async getTicketMaster(paramDto: ParamDto) { + const ticketMaster = await this.ticketMasterRepository.findByIdWithUser(paramDto.id); + if (!ticketMaster) throw new BadRequestException(TicketMessageEnum.MASTER_NOT_FOUND); + return { ticketMaster }; + } + + //******************************** */ + + async deleteTicketMaster(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) { + const ticketMaster = await this.ticketMasterRepository.findByIdWithUser(paramDto.id); + if (!ticketMaster) throw new BadRequestException(TicketMessageEnum.MASTER_NOT_FOUND); + + await this.ticketMasterRepository.delete(paramDto.id); + + await this.accessLogService.logDelete("TicketMaster", paramDto.id, UserType.ADMIN, { + user: { id: userIpAndHeaders.userId }, + endpoint: "/tickets/masters", + requestId: userIpAndHeaders.headers["x-request-id"], + ipAddress: userIpAndHeaders.ip, + userAgent: userIpAndHeaders.headers["user-agent"], + actionDescription: `Admin deleted ticket master: ${paramDto.id}`, + metadata: { + ticketMasterId: ticketMaster.id, + masterUserId: ticketMaster.user.id, + }, + }); + + return { + message: TicketMessageEnum.MASTER_DELETED, + }; + } + //******************************** */ private async checkTicketLimit(userId: string, queryRunner: QueryRunner): Promise { @@ -539,7 +615,7 @@ export class TicketsService implements OnModuleInit { //***************************** */ private async assignTicketAndNotify(ticket: Ticket, ticketCategory: TicketCategory, user: User, queryRunner: QueryRunner) { - const assignedUser = await this.referralService.assignAdmin(ticketCategory.group); + const assignedUser = (await this.assignFromTicketMasters()) ?? (await this.referralService.assignAdmin(ticketCategory.group)); // ticket.assignedTo = assignedUser; await queryRunner.manager.save(Ticket, ticket); @@ -562,6 +638,22 @@ export class TicketsService implements OnModuleInit { }); await this.notifySuperAdminsForTicket(queryRunner, ticket); } + + //***************************** */ + + private async assignFromTicketMasters(): Promise { + const masters = await this.ticketMasterRepository.findAllWithUser(); + if (!masters.length) return null; + + const mastersWithLoad = await Promise.all( + masters.map(async (master) => ({ + user: master.user, + assignedCount: await this.ticketsRepository.count({ where: { assignedTo: { id: master.user.id } } }), + })), + ); + + return mastersWithLoad.reduce((prev, curr) => (prev.assignedCount < curr.assignedCount ? prev : curr)).user; + } //***************************** */ private async findTicket(queryRunner: QueryRunner, ticketId: string, userId: string, isAdmin: boolean): Promise { diff --git a/src/modules/tickets/repositories/ticket-master.repository.ts b/src/modules/tickets/repositories/ticket-master.repository.ts new file mode 100644 index 0000000..02b4202 --- /dev/null +++ b/src/modules/tickets/repositories/ticket-master.repository.ts @@ -0,0 +1,33 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { TicketMaster } from "../entities/ticket-master.entity"; + +@Injectable() +export class TicketMasterRepository extends Repository { + constructor(@InjectRepository(TicketMaster) ticketMasterRepository: Repository) { + super(ticketMasterRepository.target, ticketMasterRepository.manager, ticketMasterRepository.queryRunner); + } + + findAllWithUser(): Promise { + return this.find({ + relations: { user: true }, + order: { createdAt: "ASC" }, + }); + } + + findByIdWithUser(id: string): Promise { + return this.findOne({ + where: { id }, + relations: { user: true }, + }); + } + + findByUserId(userId: string): Promise { + return this.findOne({ + where: { user: { id: userId } }, + relations: { user: true }, + }); + } +} diff --git a/src/modules/tickets/tickets.controller.ts b/src/modules/tickets/tickets.controller.ts index ca906f2..9d05216 100755 --- a/src/modules/tickets/tickets.controller.ts +++ b/src/modules/tickets/tickets.controller.ts @@ -1,7 +1,8 @@ -import { Body, Controller, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common"; +import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Patch, Post, Query } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto"; +import { CreateTicketMasterDto } from "./DTO/create-ticket-master.dto"; import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto"; import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto"; import { ReferTicketDto } from "./DTO/refer-ticket.dto"; @@ -16,6 +17,7 @@ import { IUserIpAndHeaders, UserDec, UserIpAndHeaders } from "../../common/decor import { ParamDto } from "../../common/DTO/param.dto"; import { PermissionEnum } from "../users/enums/permission.enum"; import { ITokenPayload } from "../auth/interfaces/IToken-payload"; + @Controller("tickets") @ApiTags("Tickets") @AuthGuards() @@ -65,6 +67,40 @@ export class TicketsController { return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders); } + //----------------- ticket masters ------------------- + + @ApiOperation({ summary: "Create ticket master => admin route" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.TICKET_MASTERS_CREATE) + @Post("masters") + createTicketMaster(@Body() createDto: CreateTicketMasterDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + return this.ticketsService.createTicketMaster(createDto, userIpAndHeaders); + } + + @ApiOperation({ summary: "Get all ticket masters => admin route" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.TICKET_MASTERS_READ) + @Get("masters") + getTicketMasters() { + return this.ticketsService.getTicketMasters(); + } + + @ApiOperation({ summary: "Get ticket master by id => admin route" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.TICKET_MASTERS_READ) + @Get("masters/:id") + getTicketMaster(@Param() paramDto: ParamDto) { + return this.ticketsService.getTicketMaster(paramDto); + } + + @ApiOperation({ summary: "Delete ticket master => admin route" }) + @AdminRoute() + @PermissionsDec(PermissionEnum.TICKET_MASTERS_DELETE) + @Delete("masters/:id") + deleteTicketMaster(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + return this.ticketsService.deleteTicketMaster(paramDto, userIpAndHeaders); + } + //----------------- ticket and ticket messages ------------------- @ApiOperation({ summary: "create ticket ==> user route" }) diff --git a/src/modules/tickets/tickets.module.ts b/src/modules/tickets/tickets.module.ts index cf86fbb..4084322 100755 --- a/src/modules/tickets/tickets.module.ts +++ b/src/modules/tickets/tickets.module.ts @@ -4,10 +4,12 @@ import { TypeOrmModule } from "@nestjs/typeorm"; import { TICKET } from "./constant"; import { TicketCategory } from "./entities/ticket-category.entity"; +import { TicketMaster } from "./entities/ticket-master.entity"; import { TicketMessage } from "./entities/ticket-message.entity"; import { Ticket } from "./entities/ticket.entity"; import { ReferralStrategyFactory } from "./providers/referral-strategy.factory"; import { TicketsService } from "./providers/tickets.service"; +import { TicketMasterRepository } from "./repositories/ticket-master.repository"; import { TicketCategoryRepository } from "./repositories/tickets-category.repository"; import { TicketMessagesRepository } from "./repositories/tickets-message.repository"; import { TicketsRepository } from "./repositories/tickets.repository"; @@ -27,7 +29,7 @@ import { LoggerModule } from "../logger/logger.module"; @Module({ imports: [ - TypeOrmModule.forFeature([Ticket, TicketCategory, TicketMessageAttachment, TicketMessage]), + TypeOrmModule.forFeature([Ticket, TicketCategory, TicketMaster, TicketMessageAttachment, TicketMessage]), AccessLogsModule, BullModule.registerQueue({ name: TICKET.QUEUE_NAME }), UsersModule, @@ -48,6 +50,7 @@ import { LoggerModule } from "../logger/logger.module"; TicketsService, TicketsRepository, TicketCategoryRepository, + TicketMasterRepository, TicketMessagesRepository, TicketMessageAttachmentsRepository, TicketProcessor, diff --git a/src/modules/users/constants/permission-meta.ts b/src/modules/users/constants/permission-meta.ts index fbec28b..d921db0 100644 --- a/src/modules/users/constants/permission-meta.ts +++ b/src/modules/users/constants/permission-meta.ts @@ -57,6 +57,11 @@ export const permissionMeta: Record = { [PermissionEnum.TICKET_CATEGORIES_UPDATE]: { title: "ویرایش دسته‌بندی تیکت", group: PermissionGroupEnum.SUPPORT }, [PermissionEnum.TICKET_CATEGORIES_DELETE]: { title: "حذف دسته‌بندی تیکت", group: PermissionGroupEnum.SUPPORT }, + [PermissionEnum.TICKET_MASTERS_CREATE]: { title: "ایجاد مستر تیکت", group: PermissionGroupEnum.SUPPORT }, + [PermissionEnum.TICKET_MASTERS_READ]: { title: "مشاهده مستر تیکت", group: PermissionGroupEnum.SUPPORT }, + [PermissionEnum.TICKET_MASTERS_UPDATE]: { title: "ویرایش مستر تیکت", group: PermissionGroupEnum.SUPPORT }, + [PermissionEnum.TICKET_MASTERS_DELETE]: { title: "حذف مستر تیکت", group: PermissionGroupEnum.SUPPORT }, + [PermissionEnum.CRITICISMS_CREATE]: { title: "ایجاد انتقادات", group: PermissionGroupEnum.OTHER }, [PermissionEnum.CRITICISMS_READ]: { title: "مشاهده انتقادات", group: PermissionGroupEnum.OTHER }, [PermissionEnum.CRITICISMS_UPDATE]: { title: "ویرایش انتقادات", group: PermissionGroupEnum.OTHER }, diff --git a/src/modules/users/enums/permission.enum.ts b/src/modules/users/enums/permission.enum.ts index 81c8014..b3306ee 100755 --- a/src/modules/users/enums/permission.enum.ts +++ b/src/modules/users/enums/permission.enum.ts @@ -59,6 +59,12 @@ export enum PermissionEnum { TICKET_CATEGORIES_UPDATE = "ticket_categories_update", TICKET_CATEGORIES_DELETE = "ticket_categories_delete", + // ticket_masters + TICKET_MASTERS_CREATE = "ticket_masters_create", + TICKET_MASTERS_READ = "ticket_masters_read", + TICKET_MASTERS_UPDATE = "ticket_masters_update", + TICKET_MASTERS_DELETE = "ticket_masters_delete", + // criticisms CRITICISMS_CREATE = "criticisms_create", CRITICISMS_READ = "criticisms_read", diff --git a/src/modules/users/providers/admins.service.ts b/src/modules/users/providers/admins.service.ts index afd5a85..5779f50 100755 --- a/src/modules/users/providers/admins.service.ts +++ b/src/modules/users/providers/admins.service.ts @@ -277,7 +277,11 @@ export class AdminsService { paginate: true, }; } + /************************************************************ */ + async getAllAdmins() { + return this.userRepository.find({ where: { roles: { isAdmin: true } } }); + } /************************************************************ */ async getAdminById(id: string) { diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 6db88fd..4a5c4f5 100755 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -171,12 +171,18 @@ export class UsersController { @ApiOperation({ summary: "get all admins ==> admin route" }) @AdminRoute() - // @PermissionsDec(PermissionEnum.ADMINS_READ, PermissionEnum.CUSTOMERS_READ) @Get("admins") getAdmins(@Query() queryDto: SearchAdminQueryDto) { return this.adminsService.getAdmins(queryDto); } + @ApiOperation({ summary: "get all admins without pagination==> admin route" }) + @AdminRoute() + @Get("admins-list/all") + getAllAdmins() { + return this.adminsService.getAllAdmins(); + } + @ApiOperation({ summary: "get admin by id ==> admin route" }) @PermissionsDec(PermissionEnum.ADMINS_READ, PermissionEnum.CUSTOMERS_READ) @Get("admins/:id")