diff --git a/src/common/DTO/pagination.dto.ts b/src/common/DTO/pagination.dto.ts new file mode 100755 index 0000000..6dd4287 --- /dev/null +++ b/src/common/DTO/pagination.dto.ts @@ -0,0 +1,22 @@ +import { ApiPropertyOptional } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { IsNotEmpty, IsNumber, IsOptional, Max, Min } from "class-validator"; + +export class PaginationDto { + @IsOptional() + @IsNotEmpty() + @IsNumber() + @Min(1) + @Type(() => Number) + @ApiPropertyOptional({ type: "number", required: false }) + page?: number; + + @IsOptional() + @IsNotEmpty() + @IsNumber() + @Min(1) + @Max(50) + @Type(() => Number) + @ApiPropertyOptional({ type: "number", required: false }) + limit?: number; +} diff --git a/src/common/DTO/param.dto.ts b/src/common/DTO/param.dto.ts new file mode 100755 index 0000000..8a84660 --- /dev/null +++ b/src/common/DTO/param.dto.ts @@ -0,0 +1,19 @@ +import { ApiProperty, PartialType } from "@nestjs/swagger"; +import { IsNotEmpty, IsUUID } from "class-validator"; + +import { CommonMessage } from "../enums/message.enum"; + +export class ParamDto { + @IsNotEmpty({ message: CommonMessage.ID_REQUIRED }) + @IsUUID("4", { message: CommonMessage.ID_SHOULD_BE_UUID }) + @ApiProperty({ description: "Id of the entity", example: "8b1e8b1e-8b1e-8b1e-8b1e-8b1e8b1e8b1e" }) + id: string; +} + +export class OptionalParamDto extends PartialType(ParamDto) {} + +export class ParamNumberIdDto { + @IsNotEmpty({ message: CommonMessage.ID_REQUIRED }) + @ApiProperty({ description: "Id of the entity", example: "20" }) + id: string; +} diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index 4eb1e19..2c0eaf3 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -71,6 +71,54 @@ export const enum AuthMessage { INVALID_SLUG = 'اسلاگ نامعتبر است', INVALID_OR_EXPIRED_TOKEN = 'توکن نامعتبر یا منقضی شده است', } + +export const enum TicketMessageEnum { + TITLE_REQUIRED = "عنوان تیکت مورد نیاز است", + TITLE_STRING = "عنوان تیکت باید یک رشته باشد", + TITLE_LENGTH = "عنوان تیکت باید بین ۳ تا ۱۵۰ کاراکتر باشد", + SUBJECT_REQUIRED = "موضوع تیکت اجباری می‌باشد", + SUBJECT_STRING = "موضوع تیکت باید رشته باشد", + SUBJECT_LENGTH = "موضوع تیکت باید بین ۳ تا ۱۵۰ کاراکتر باشد", + PRIORITY_REQUIRED = "اولویت تیکت اجباری می‌باشد", + PRIORITY_INVALID = "اولویت تیکت باید یکی از مقادیر LOW، MEDIUM یا HIGH باشد", + CATEGORY_ID_REQUIRED = "شناسه دسته‌بندی نمی‌تواند خالی باشد", + CATEGORY_ID_SHOULD_BE_UUID = "شناسه دسته‌بندی باید یک UUID معتبر باشد", + DANAK_SERVICE_ID_REQUIRED = "شناسه سرویس داناک اجباری است", + DANAK_SERVICE_ID_SHOULD_BE_UUID = "شناسه سرویس داناک باید یک UUID معتبر باشد", + MESSAGE_REQUIRED = "متن پیام تیکت اجباری است", + MESSAGE_STRING = "متن پیام باید یک رشته باشد", + MESSAGE_LENGTH = "متن پیام باید بین ۵ تا ۵۰۰۰ کاراکتر باشد", + CATEGORY_EXIST = "دسته‌بندی با این نام قبلاً ثبت شده است", + CATEGORY_CREATED = "دسته‌بندی با موفقیت ایجاد شد", + CATEGORY_NOT_FOUND = "دسته‌بندی یافت نشد", + USER_GROUP_ID_NOT_EMPTY = "شناسه گروه کاربری نمی‌تواند خالی باشد", + USER_GROUP_ID_SHOULD_BE_UUID = "شناسه گروه کاربری باید یک UUID معتبر باشد", + IS_ACTIVE_REQUIRED = "وضعیت تیکت مورد نیاز است", + IS_ACTIVE_SHOULD_BE_BOOLEAN = "وضعیت تیکت باید یک مقدار بولی (true/false) باشد", + CREATED = "تیکت با موفقیت ساخته شد", + DESCRIPTION_REQUIRED = "توضیحات تیکت مورد نیاز است", + DESCRIPTION_STRING = "توضیحات تیکت باید یک رشته باشد", + DESCRIPTION_LENGTH = "توضیحات تیکت باید بین ۳ تا ۵۰۰ کاراکتر باشد", + CONTENT_REQUIRED = "محتوای پیام تیکت مورد نیاز است", + ATTACHMENT_REQUIRED = "آدرس فایل پیوست تیکت مورد نیاز است", + ATTACHMENT_SHOULD_BE_URL = "آدرس فایل پیوست تیکت باید یوآرال باشد", + CONTENT_STRING = "محتوای پیام تیکت باید یک رشته باشد", + MESSAGE_CREATED = "پیام تیکت با موفقیت ایجاد شد", + TICKET_NOT_FOUND = "تیکت مورد نظر یافت نشد", + CLOSED = "تیکت با موفقیت بسته شد", + TICKET_CLOSED = "تیکت قبلا بسته شده است", + ERROR_IN_TICKET_MSG_CREATION = "خطا در ایجاد پیام تیکت", + SERVICE_NOT_FOUND = "سرویس مورد نظر یافت نشد", + TICKET_NOT_ASSIGNED = "تیکت به کارشناسی اختصاص داده نشده است", + TICKET_NOT_ASSIGNED_TO_USER = "تیکت به شما اختصاص داده نشده است", + REFERRED = "REFERRED", + NO_ACTIVE_SUPPORT_PLAN = "شما پلن پشتیبانی فعال ندارید", + TICKET_LIMIT_NOT_FOUND = "محدودیت تعداد تیکت در پلن پشتیبانی شما یافت نشد", + INVALID_TICKET_LIMIT = "محدودیت تعداد تیکت در پلن پشتیبانی شما نامعتبر است", + TICKET_LIMIT_EXCEEDED = "شما به محدودیت تعداد تیکت در پلن پشتیبانی خود رسیده‌اید", + OPENED = "تیکت با موفقیت باز شد", +} + export const enum productMessage { NOT_FOUND = 'کالایی با این مشخصات یافت نشد', } diff --git a/src/modules/order/events/order.events.ts b/src/modules/order/events/order.events.ts index 4a75432..f7713fe 100644 --- a/src/modules/order/events/order.events.ts +++ b/src/modules/order/events/order.events.ts @@ -1,4 +1,4 @@ -import type { OrderStatus, StatusTransitionRef } from '../interface/order.interface'; +import type { OrderStatus } from '../interface/order.interface'; export class OrderCreatedEvent { constructor( @@ -17,6 +17,5 @@ export class OrderStatusChangedEvent { public readonly: string, public readonly previousStatus: OrderStatus, public readonly newStatus: OrderStatus, - public readonly changedBy: StatusTransitionRef, ) { } } diff --git a/src/modules/order/providers/orders.service.ts b/src/modules/order/providers/orders.service.ts index 5fc7f06..c276182 100644 --- a/src/modules/order/providers/orders.service.ts +++ b/src/modules/order/providers/orders.service.ts @@ -6,7 +6,6 @@ import { User } from '../../user/entities/user.entity'; import { PaymentMethodEnum, PaymentStatusEnum } from '../../payment/interface/payment'; -import { PaymentMethod } from '../../payment/entities/payment-method.entity'; import { PaymentsService } from '../../payment/services/payments.service'; import { OrderRepository } from '../repositories/order.repository'; diff --git a/src/modules/payment/controllers/payments.controller.ts b/src/modules/payment/controllers/payments.controller.ts index 4d9bee9..31789d0 100644 --- a/src/modules/payment/controllers/payments.controller.ts +++ b/src/modules/payment/controllers/payments.controller.ts @@ -10,8 +10,7 @@ import { ApiHeader, } from '@nestjs/swagger'; import { AdminAuthGuard } from 'src/modules/auth/guards/adminAuth.guard'; -import { PaymentMethodService } from '../services/payment-method.service'; -import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto'; + import { CreatePaymentMethodDto } from '../dto/create-payment-method.dto'; import { UpdatePaymentMethodDto } from '../dto/update-payment-method.dto'; import { VerifyPaymentDto } from '../dto/verify-payment.dto'; import { PaymentChartDto } from '../dto/payment-chart.dto'; @@ -26,31 +25,30 @@ import { Permission } from 'src/common/enums/permission.enum'; export class PaymentsController { constructor( private readonly paymentsService: PaymentsService, - private readonly paymentMethodService: PaymentMethodService, - ) { } + ) { } - @UseGuards(AuthGuard) - @ApiBearerAuth() - @Get('public/payments/pay-order/:orderId') - @ApiOperation({ summary: 'Pay for an order' }) - @ApiParam({ name: 'orderId', type: 'string', description: 'Order ID' }) + // @UseGuards(AuthGuard) + // @ApiBearerAuth() + // @Get('public/payments/pay-order/:orderId') + // @ApiOperation({ summary: 'Pay for an order' }) + // @ApiParam({ name: 'orderId', type: 'string', description: 'Order ID' }) - payAnOrder(@Param('orderId') orderId: string) { - return this.paymentsService.payOrder(orderId); - } + // payAnOrder(@Param('orderId') orderId: string) { + // return this.paymentsService.payOrder(orderId); + // } - @Post('public/payments/verify') - @ApiOperation({ summary: 'Verify a payment' }) + // @Post('public/payments/verify') + // @ApiOperation({ summary: 'Verify a payment' }) - @ApiBody({ type: VerifyPaymentDto }) - @ApiNotFoundResponse({ description: 'Payment not found' }) - verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) { - return this.paymentsService.verifyOnlinePayment(verifyPaymentDto.authority, verifyPaymentDto.orderId); - } + // @ApiBody({ type: VerifyPaymentDto }) + // @ApiNotFoundResponse({ description: 'Payment not found' }) + // verifyPayment(@Body() verifyPaymentDto: VerifyPaymentDto) { + // return this.paymentsService.verifyOnlinePayment(verifyPaymentDto.authority, verifyPaymentDto.orderId); + // } diff --git a/src/modules/ticket/DTO/create-ticket.dto.ts b/src/modules/ticket/DTO/create-ticket.dto.ts index 822c0fb..ac4108f 100755 --- a/src/modules/ticket/DTO/create-ticket.dto.ts +++ b/src/modules/ticket/DTO/create-ticket.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty } from "@nestjs/swagger"; import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum"; -import { TicketPriority } from "../enums/ticket-priority.enum"; + export class CreateTicketDto { // @IsNotEmpty({ message: TicketMessageEnum.TITLE_REQUIRED }) @@ -17,10 +17,6 @@ export class CreateTicketDto { @ApiProperty({ description: "Subject of the ticket", example: "Subject" }) subject: string; - @IsNotEmpty({ message: TicketMessageEnum.PRIORITY_REQUIRED }) - @IsEnum(TicketPriority, { message: TicketMessageEnum.PRIORITY_INVALID }) - @ApiProperty({ enum: TicketPriority, example: TicketPriority.LOW, description: "LOW, MEDIUM, HIGH" }) - priority: TicketPriority; @IsOptional() @IsNotEmpty({ message: TicketMessageEnum.DANAK_SERVICE_ID_REQUIRED }) @@ -48,8 +44,8 @@ export class CreateTicketDto { } export class CreateTicketByAdminDto extends CreateTicketDto { - @IsNotEmpty({ message: UserMessage.USER_ID_REQUIRED }) - @IsUUID("4", { message: UserMessage.USER_ID_SHOULD_BE_A_UUID }) + @IsNotEmpty({ message: "UserMessage.USER_ID_REQUIRED" }) + @IsUUID("4", { message:" UserMessage.USER_ID_SHOULD_BE_A_UUID" }) @ApiProperty({ description: "User ID of the ticket", example: "123e4567-e89b-12d3-a456-426614174000" }) userId: string; } diff --git a/src/modules/ticket/DTO/search-ticket-category.dto.ts b/src/modules/ticket/DTO/search-ticket-category.dto.ts index 17e0593..f1af126 100755 --- a/src/modules/ticket/DTO/search-ticket-category.dto.ts +++ b/src/modules/ticket/DTO/search-ticket-category.dto.ts @@ -1,3 +1,3 @@ -import { CategoryListSearchQueryDto } from "../../danak-services/DTO/category-search-query.dto"; + -export class SearchTicketCategoryDto extends CategoryListSearchQueryDto {} +export class SearchTicketCategoryDto {} diff --git a/src/modules/ticket/DTO/search-ticket-query.dto.ts b/src/modules/ticket/DTO/search-ticket-query.dto.ts index 8a53620..6f8c461 100755 --- a/src/modules/ticket/DTO/search-ticket-query.dto.ts +++ b/src/modules/ticket/DTO/search-ticket-query.dto.ts @@ -12,7 +12,7 @@ export class SearchTicketQueryDto extends PaginationDto { status?: TicketStatus; @IsOptional() - @IsUUID("4", { message: UserMessage.USER_ID_SHOULD_BE_A_UUID }) + @IsUUID("4", { message:" UserMessage.USER_ID_SHOULD_BE_A_UUID" }) @ApiPropertyOptional({ description: "user id", example: "123e4567-e89b-12d3-a456-426614174000" }) userId?: string; } diff --git a/src/modules/ticket/providers/tickets.service.ts b/src/modules/ticket/providers/tickets.service.ts index f124f82..5e87c86 100755 --- a/src/modules/ticket/providers/tickets.service.ts +++ b/src/modules/ticket/providers/tickets.service.ts @@ -2,638 +2,638 @@ import { InjectQueue } from "@nestjs/bullmq"; import { BadRequestException, HttpException, Injectable, InternalServerErrorException, Logger, OnModuleInit } from "@nestjs/common"; import { Queue } from "bullmq"; import dayjs from "dayjs"; -import { DataSource, MoreThanOrEqual, Not, QueryRunner } from "typeorm"; +// import { DataSource, MoreThanOrEqual, Not, QueryRunner } from "typeorm"; -import { TICKET } from "../constant"; -import { ReferralService } from "./referral.service"; -import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator"; -import { ParamDto } from "../../../common/DTO/param.dto"; -import { CommonMessage, TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum"; -import { UserType } from "../../access-logs/enums/user-type.enum"; -import { AccessLogService } from "../../access-logs/providers/access-log.service"; -import { DanakService } from "../../danak-services/entities/danak-service.entity"; -import { NotificationQueue } from "../../notifications/queue/notification.queue"; -import { SupportPlanFeatureKey } from "../../support-plans/enums/support-plan-feature-key.enum"; -import { SupportPlansService } from "../../support-plans/providers/support-plans.service"; -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 { CreateTicketMessageDto } from "../DTO/create-ticket-message.dto"; -import { CreateTicketByAdminDto, CreateTicketDto } from "../DTO/create-ticket.dto"; -import { ReferTicketDto } from "../DTO/refer-ticket.dto"; -import { SearchTicketCategoryDto } from "../DTO/search-ticket-category.dto"; -import { SearchTicketQueryDto } from "../DTO/search-ticket-query.dto"; -import { UpdateTicketCategoryDto } from "../DTO/update-ticket-category.dto"; -import { TicketCategory } from "../entities/ticket-category.entity"; -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 { TicketCategoryRepository } from "../repositories/tickets-category.repository"; -import { TicketMessagesRepository } from "../repositories/tickets-message.repository"; -import { TicketsRepository } from "../repositories/tickets.repository"; +// import { TICKET } from "../constant"; +// import { ReferralService } from "./referral.service"; +// import { IUserIpAndHeaders } from "../../../common/decorators/user.decorator"; +// import { ParamDto } from "../../../common/DTO/param.dto"; +// import { CommonMessage, TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum"; +// import { UserType } from "../../access-logs/enums/user-type.enum"; +// import { AccessLogService } from "../../access-logs/providers/access-log.service"; +// import { DanakService } from "../../danak-services/entities/danak-service.entity"; +// import { NotificationQueue } from "../../notifications/queue/notification.queue"; +// import { SupportPlanFeatureKey } from "../../support-plans/enums/support-plan-feature-key.enum"; +// import { SupportPlansService } from "../../support-plans/providers/support-plans.service"; +// 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 { CreateTicketMessageDto } from "../DTO/create-ticket-message.dto"; +// import { CreateTicketByAdminDto, CreateTicketDto } from "../DTO/create-ticket.dto"; +// import { ReferTicketDto } from "../DTO/refer-ticket.dto"; +// import { SearchTicketCategoryDto } from "../DTO/search-ticket-category.dto"; +// import { SearchTicketQueryDto } from "../DTO/search-ticket-query.dto"; +// import { UpdateTicketCategoryDto } from "../DTO/update-ticket-category.dto"; +// import { TicketCategory } from "../entities/ticket-category.entity"; +// 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 { TicketCategoryRepository } from "../repositories/tickets-category.repository"; +// import { TicketMessagesRepository } from "../repositories/tickets-message.repository"; +// import { TicketsRepository } from "../repositories/tickets.repository"; @Injectable() -export class TicketsService implements OnModuleInit { +export class TicketsService { private readonly logger = new Logger(TicketsService.name); - constructor( - @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue, - private readonly ticketsRepository: TicketsRepository, - private readonly ticketsCategoryRepository: TicketCategoryRepository, - private readonly ticketMessagesRepository: TicketMessagesRepository, - private readonly usersService: UsersService, - private readonly adminsService: AdminsService, - private readonly notificationQueue: NotificationQueue, - private readonly referralService: ReferralService, - private readonly dataSource: DataSource, - private readonly supportPlansService: SupportPlansService, - private readonly accessLogService: AccessLogService, - ) {} - - async onModuleInit() { - await this.scheduleAutoCloseJob(); - } - - //******************************** */ - async getTicketCategories() { - const ticketCategories = await this.ticketsCategoryRepository.find({}); - return { - ticketCategories, - }; - } - //******************************** */ - - async getCategoriesList(queryDto: SearchTicketCategoryDto) { - const [categories, count] = await this.ticketsCategoryRepository.findCategoriesList(queryDto); - - return { categories, count, paginate: true }; - } - //******************************** */ - async createTicketCategory(createDto: CreateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) { - const { userGroup } = await this.usersService.getUserGroupById(createDto.userGroupId); - const existCategory = await this.ticketsCategoryRepository.findCategoryByTitle(createDto.title); - - if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST); - const ticketCategory = this.ticketsCategoryRepository.create({ - ...createDto, - group: userGroup, - }); - - await this.ticketsCategoryRepository.save(ticketCategory); - - await this.accessLogService.logCreate("TicketCategory", UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets/categories", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin created ticket category: ${createDto.title}`, - metadata: { - categoryId: ticketCategory.id, - categoryTitle: ticketCategory.title, - }, - }); - - return { - message: TicketMessageEnum.CATEGORY_CREATED, - }; - } - - //******************************** */ - async updateCategory(paramDto: ParamDto, updateCategoryDto: UpdateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) { - const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id); - if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); - - if (updateCategoryDto.title) { - const { title } = updateCategoryDto; - const existCategory = await this.ticketsCategoryRepository.findOneBy({ - title, - id: Not(paramDto.id), - }); - if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST); - } - await this.ticketsCategoryRepository.save({ - ...category, - ...updateCategoryDto, - }); - - await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets/categories", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin updated ticket category: ${category.title}`, - metadata: { - categoryId: category.id, - categoryTitle: category.title, - }, - oldValues: JSON.stringify({ ...category }), - newValues: JSON.stringify({ ...updateCategoryDto }), - }); - - return { - message: CommonMessage.UPDATE_SUCCESS, - }; - } - //******************************** */ - async toggleCategoryStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) { - const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id); - if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); - // - category.isActive = !category.isActive; - await this.ticketsCategoryRepository.save(category); - // - await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets/categories", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin toggled ticket category status: ${category.title}`, - metadata: { - categoryId: category.id, - categoryTitle: category.title, - isActive: category.isActive, - }, - }); - - return { - message: CommonMessage.UPDATE_SUCCESS, - isActive: category.isActive, - }; - } - - //******************************** */ - - private async checkTicketLimit(userId: string, queryRunner: QueryRunner): Promise { - const { userSupportPlan } = await this.supportPlansService.getUserSupportPlanOfUser(userId); - - if (!userSupportPlan) throw new BadRequestException(TicketMessageEnum.NO_ACTIVE_SUPPORT_PLAN); - - const ticketLimitFeature = userSupportPlan.supportPlan.features.find( - (feature) => feature.featureKey === SupportPlanFeatureKey.TICKET_LIMIT, - ); - - if (!ticketLimitFeature) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_NOT_FOUND); - - const ticketLimit = parseInt(ticketLimitFeature.featureValue); - if (isNaN(ticketLimit)) return; - - const today = dayjs().startOf("day").toDate(); - - const ticketCount = await queryRunner.manager.count(this.ticketsRepository.target, { - where: { - user: { id: userId }, - createdAt: MoreThanOrEqual(today), - }, - }); - - if (ticketCount >= ticketLimit) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_EXCEEDED); - } - - //******************************** */ - - async createTicket(createDto: CreateTicketDto, userId: string) { - const queryRunner = this.dataSource.createQueryRunner(); - - try { - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const [user, ticketCategory] = await Promise.all([ - this.usersService.findOneByIdWithQueryRunner(userId, queryRunner), - queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }), - ]); - - if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); - - await this.checkTicketLimit(userId, queryRunner); - - let service = null; - if (createDto.danakServiceId) { - service = await queryRunner.manager.findOneBy(DanakService, { id: createDto.danakServiceId }); - if (!service) throw new BadRequestException(TicketMessageEnum.SERVICE_NOT_FOUND); - } - - const ticketMessage: Partial = { content: createDto.message, author: user }; - - if (createDto.attachmentUrls?.length) { - ticketMessage.attachments = createDto.attachmentUrls.map((url) => { - const attachment = new TicketMessageAttachment(); - attachment.attachmentUrl = url; - return attachment; - }); - } - - const ticket = queryRunner.manager.create(Ticket, { - ...createDto, - user, - category: ticketCategory, - messages: [ticketMessage], - danakService: service, - }); - - await queryRunner.manager.save(Ticket, ticket); - - await this.assignTicketAndNotify(ticket, ticketCategory, user, queryRunner); - - await queryRunner.commitTransaction(); - - return { - message: TicketMessageEnum.CREATED, - ticketId: ticket.id, - }; - } catch (error) { - this.logger.error("Error in create ticket transaction", error); - await queryRunner.rollbackTransaction(); - throw error; - } finally { - await queryRunner.release(); - } - } - //******************************** */ - - async createTicketByAdmin(createDto: CreateTicketByAdminDto, userIpAndHeaders: IUserIpAndHeaders) { - const queryRunner = this.dataSource.createQueryRunner(); - - try { - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const [user, admin, ticketCategory] = await Promise.all([ - this.usersService.findOneByIdWithQueryRunner(createDto.userId, queryRunner), - this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner), - queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }), - ]); - - if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); - - const ticketMessage: Partial = { content: createDto.message, author: admin }; - - const ticket = queryRunner.manager.create(Ticket, { - ...createDto, - user, - category: ticketCategory, - messages: [ticketMessage], - assignedTo: admin, - }); - - await queryRunner.manager.save(Ticket, ticket); - - await this.notificationQueue.addTicketNotification(user.id, { - ticketId: ticket.numericId.toString(), - subject: ticket.subject, - date: ticket.createdAt, - userPhone: user.phone, - userEmail: user.email, - }); - - await this.accessLogService.logCreate("Ticket", UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin created ticket: ${ticket.subject}`, - metadata: { - ticketId: ticket.id, - ticketSubject: ticket.subject, - }, - }); - - await queryRunner.commitTransaction(); - - return { - message: TicketMessageEnum.CREATED, - ticketId: ticket.id, - }; - } catch (error) { - await queryRunner.rollbackTransaction(); - throw error; - } finally { - await queryRunner.release(); - } - } - - //******************************** */ - - async getTickets(queryDto: SearchTicketQueryDto, userId: string) { - const [tickets, count] = await this.ticketsRepository.findTicketsList(queryDto, userId); - - return { tickets, count, paginate: true }; - } - //******************************** */ - async getTicketForAdmin(queryDto: SearchTicketQueryDto, adminId: string) { - const isSuperAdmin = await this.usersService.isSuperAdmin(adminId); - - const [tickets, count] = await this.ticketsRepository.findTicketsListForAdmin(queryDto, adminId, isSuperAdmin); - - return { tickets, count, paginate: true }; - } - - //******************************** */ - - async createTicketMessage(ticketId: string, createDto: CreateTicketMessageDto, userId: string, isAdmin: boolean) { - const queryRunner = this.dataSource.createQueryRunner(); - await queryRunner.connect(); - await queryRunner.startTransaction(); - - try { - const ticket = await this.findTicket(queryRunner, ticketId, userId, isAdmin); - if (ticket.status === TicketStatus.CLOSED) throw new BadRequestException(TicketMessageEnum.TICKET_CLOSED); - - const isSuperAdmin = await this.usersService.isSuperAdmin(userId); - if (isAdmin && !isSuperAdmin && ticket.assignedTo?.id !== userId) - throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER); - - const user = await queryRunner.manager.findOneBy(User, { id: userId }); - if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); - - const ticketMessage = queryRunner.manager.create(TicketMessage, { - content: createDto.content, - author: user, - ticket, - }); - await queryRunner.manager.save(TicketMessage, ticketMessage); - - if (createDto.attachmentUrls?.length) { - await this.saveAttachments(queryRunner, createDto.attachmentUrls, ticketMessage); - } - - this.updateTicketStatus(ticket, isAdmin); - await queryRunner.manager.save(Ticket, ticket); - - if (isAdmin) { - await this.sendNotificationForAdminResponse(ticket, user); - } - - await queryRunner.commitTransaction(); - return { - message: TicketMessageEnum.MESSAGE_CREATED, - ticketMessage, - }; - } catch (error) { - await queryRunner.rollbackTransaction(); - if (error instanceof HttpException) throw error; - throw new InternalServerErrorException(TicketMessageEnum.ERROR_IN_TICKET_MSG_CREATION); - } finally { - await queryRunner.release(); - } - } - - //******************************** */ - - async getTicketMessages(ticketId: string, userId: string, isAdmin: boolean) { - let ticket: null | Ticket = null; - // - if (isAdmin) { - ticket = await this.ticketsRepository.findTicketById(ticketId); - } else { - ticket = await this.ticketsRepository.findTicketById(ticketId, userId); - } - if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); - - const messages = await this.ticketMessagesRepository.findMessagesByTicketId(ticketId); - const userSupportPlan = await this.supportPlansService.getUserSupportPlanOfUser(ticket.user.id); - return { - ticket, - messages, - supportPlan: userSupportPlan, - }; - } - - //******************************** */ - - async closeTicketByUser(ticketId: string, isAdmin: boolean, userIpAndHeaders: IUserIpAndHeaders) { - let ticket: null | Ticket = null; - - if (isAdmin) { - ticket = await this.ticketsRepository.findTicketById(ticketId); - } else { - ticket = await this.ticketsRepository.findTicketById(ticketId, userIpAndHeaders.userId); - } - - if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); - - ticket.status = TicketStatus.CLOSED; - await this.ticketsRepository.save(ticket); - - await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin closed ticket: ${ticket.subject}`, - metadata: { - ticketId: ticket.id, - ticketSubject: ticket.subject, - }, - }); - - return { - message: TicketMessageEnum.CLOSED, - }; - } - //******************************** * / - async openTicketByAdmin(ticketId: string, userIpAndHeaders: IUserIpAndHeaders) { - const ticket = await this.ticketsRepository.findTicketById(ticketId); - if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); - - ticket.status = TicketStatus.PENDING; - await this.ticketsRepository.save(ticket); - - await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin opened ticket: ${ticket.subject}`, - metadata: { - ticketId: ticket.id, - ticketSubject: ticket.subject, - }, - }); - - return { - message: TicketMessageEnum.OPENED, - }; - } - - //******************************** */ - async referTicket(ticketId: string, referDto: ReferTicketDto, userIpAndHeaders: IUserIpAndHeaders) { - const queryRunner = this.dataSource.createQueryRunner(); - - try { - await queryRunner.connect(); - await queryRunner.startTransaction(); - - const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId); - // if (!isSuperAdmin) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER); - console.log("isSuperAdmin", isSuperAdmin); - - const ticket = await this.ticketsRepository.findTicketById(ticketId); - if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); - - const admin = await this.usersService.findOneByIdWithQueryRunner(referDto.adminId, queryRunner); - - ticket.assignedTo = admin; - await queryRunner.manager.save(this.ticketsRepository.target, ticket); - - await this.notificationQueue.addAssignTicketNotificationForAdmin(admin.id, { - ticketId: ticket.numericId.toString(), - subject: ticket.subject, - date: ticket.createdAt, - userPhone: admin.phone, - userEmail: admin.email, - user: `${ticket.user.firstName} ${ticket.user.lastName}`, - }); - - await queryRunner.commitTransaction(); - - await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { - user: { id: userIpAndHeaders.userId }, - endpoint: "/tickets", - requestId: userIpAndHeaders.headers["x-request-id"], - ipAddress: userIpAndHeaders.ip, - userAgent: userIpAndHeaders.headers["user-agent"], - actionDescription: `Admin referred ticket: ${ticket.subject}`, - metadata: { - ticketId: ticket.id, - ticketSubject: ticket.subject, - }, - }); - return { - message: TicketMessageEnum.REFERRED, - ticket, - }; - } catch (error) { - await queryRunner.rollbackTransaction(); - throw error; - } finally { - await queryRunner.release(); - } - } - - //******************************** */ - - async getUnreadTickets() { - const unreadTickets = await this.ticketsRepository.count({ - where: { status: TicketStatus.PENDING }, - }); - return unreadTickets; - } - - //******************************** */ - async countUserTickets(userId: string) { - const ticketCount = await this.ticketsRepository.count({ - where: { user: { id: userId }, status: Not(TicketStatus.CLOSED) }, - }); - return ticketCount; - } - - //***************************** */ - private async assignTicketAndNotify(ticket: Ticket, ticketCategory: TicketCategory, user: User, queryRunner: QueryRunner) { - const assignedUser = await this.referralService.assignAdmin(ticketCategory.group); - // - ticket.assignedTo = assignedUser; - await queryRunner.manager.save(Ticket, ticket); - - await this.notificationQueue.addAssignTicketNotificationForAdmin(assignedUser.id, { - ticketId: ticket.numericId.toString(), - subject: ticket.subject, - date: ticket.createdAt, - userPhone: user.phone, - userEmail: user.email, - user: `${user.firstName} ${user.lastName}`, - }); - - await this.notificationQueue.addTicketNotification(user.id, { - ticketId: ticket.numericId.toString(), - subject: ticket.subject, - date: ticket.createdAt, - userPhone: user.phone, - userEmail: user.email, - }); - await this.notifySuperAdminsForTicket(queryRunner, ticket); - } - //***************************** */ - - private async findTicket(queryRunner: QueryRunner, ticketId: string, userId: string, isAdmin: boolean): Promise { - const ticket = isAdmin - ? await queryRunner.manager.findOne(Ticket, { - where: { id: ticketId }, - relations: { user: true, assignedTo: true }, - }) - : await queryRunner.manager.findOne(Ticket, { - where: { id: ticketId, user: { id: userId } }, - relations: { user: true, assignedTo: true }, - }); - - if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); - return ticket; - } - //***************************** */ - - private updateTicketStatus(ticket: Ticket, isAdmin: boolean): void { - if (isAdmin && ticket.status === TicketStatus.PENDING) { - ticket.status = TicketStatus.ANSWERED; - } else if (!isAdmin && ticket.status === TicketStatus.ANSWERED) { - ticket.status = TicketStatus.PENDING; - } - } - //***************************** */ - - private async saveAttachments(queryRunner: QueryRunner, attachmentUrls: string[], ticketMessage: TicketMessage) { - const attachments = attachmentUrls.map((url) => - queryRunner.manager.create(TicketMessageAttachment, { - attachmentUrl: url, - ticketMessage, - }), - ); - await queryRunner.manager.save(TicketMessageAttachment, attachments); - } - //***************************** */ - - private async sendNotificationForAdminResponse(ticket: Ticket, admin: User) { - await this.notificationQueue.addAnswerTicketNotification(ticket.user.id, { - ticketId: ticket.numericId.toString(), - subject: ticket.subject, - date: ticket.createdAt, - userPhone: admin.phone, - userEmail: admin.email, - }); - } - //***************************** */ - - private async notifySuperAdminsForTicket(queryRunner: QueryRunner, ticket: Ticket) { - const superAdmins = await this.adminsService.getSuperAdmins(queryRunner); - - for (const admin of superAdmins) { - await this.notificationQueue.addNewTicketGlobalNotification(admin.id, { - ticketId: ticket.numericId.toString(), - ticketSubject: ticket.subject, - userPhone: admin.phone, - userEmail: admin.email, - fullName: `${ticket.user.firstName} ${ticket.user.lastName}`, - }); - } - } - //***************************** */ - - async scheduleAutoCloseJob() { - await this.ticketQueue.add( - TICKET.AUTO_CLOSE_JOB_NAME, - {}, - { - repeat: { pattern: TICKET.AUTO_CLOSE_REPEAT_PATTERN }, - priority: TICKET.AUTO_CLOSE_PRIORITY, - }, - ); - this.logger.log("Scheduled auto-close-inactive job for tickets"); - } + // constructor( + // @InjectQueue(TICKET.QUEUE_NAME) private readonly ticketQueue: Queue, + // private readonly ticketsRepository: TicketsRepository, + // private readonly ticketsCategoryRepository: TicketCategoryRepository, + // private readonly ticketMessagesRepository: TicketMessagesRepository, + // private readonly usersService: UsersService, + // private readonly adminsService: AdminsService, + // private readonly notificationQueue: NotificationQueue, + // private readonly referralService: ReferralService, + // private readonly dataSource: DataSource, + // private readonly supportPlansService: SupportPlansService, + // private readonly accessLogService: AccessLogService, + // ) {} + + // async onModuleInit() { + // await this.scheduleAutoCloseJob(); + // } + + // //******************************** */ + // async getTicketCategories() { + // const ticketCategories = await this.ticketsCategoryRepository.find({}); + // return { + // ticketCategories, + // }; + // } + // //******************************** */ + + // async getCategoriesList(queryDto: SearchTicketCategoryDto) { + // const [categories, count] = await this.ticketsCategoryRepository.findCategoriesList(queryDto); + + // return { categories, count, paginate: true }; + // } + // //******************************** */ + // async createTicketCategory(createDto: CreateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) { + // const { userGroup } = await this.usersService.getUserGroupById(createDto.userGroupId); + // const existCategory = await this.ticketsCategoryRepository.findCategoryByTitle(createDto.title); + + // if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST); + // const ticketCategory = this.ticketsCategoryRepository.create({ + // ...createDto, + // group: userGroup, + // }); + + // await this.ticketsCategoryRepository.save(ticketCategory); + + // await this.accessLogService.logCreate("TicketCategory", UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets/categories", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin created ticket category: ${createDto.title}`, + // metadata: { + // categoryId: ticketCategory.id, + // categoryTitle: ticketCategory.title, + // }, + // }); + + // return { + // message: TicketMessageEnum.CATEGORY_CREATED, + // }; + // } + + // //******************************** */ + // async updateCategory(paramDto: ParamDto, updateCategoryDto: UpdateTicketCategoryDto, userIpAndHeaders: IUserIpAndHeaders) { + // const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id); + // if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); + + // if (updateCategoryDto.title) { + // const { title } = updateCategoryDto; + // const existCategory = await this.ticketsCategoryRepository.findOneBy({ + // title, + // id: Not(paramDto.id), + // }); + // if (existCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_EXIST); + // } + // await this.ticketsCategoryRepository.save({ + // ...category, + // ...updateCategoryDto, + // }); + + // await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets/categories", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin updated ticket category: ${category.title}`, + // metadata: { + // categoryId: category.id, + // categoryTitle: category.title, + // }, + // oldValues: JSON.stringify({ ...category }), + // newValues: JSON.stringify({ ...updateCategoryDto }), + // }); + + // return { + // message: CommonMessage.UPDATE_SUCCESS, + // }; + // } + // //******************************** */ + // async toggleCategoryStatus(paramDto: ParamDto, userIpAndHeaders: IUserIpAndHeaders) { + // const category = await this.ticketsCategoryRepository.findCategoryById(paramDto.id); + // if (!category) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); + // // + // category.isActive = !category.isActive; + // await this.ticketsCategoryRepository.save(category); + // // + // await this.accessLogService.logUpdate("TicketCategory", paramDto.id, UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets/categories", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin toggled ticket category status: ${category.title}`, + // metadata: { + // categoryId: category.id, + // categoryTitle: category.title, + // isActive: category.isActive, + // }, + // }); + + // return { + // message: CommonMessage.UPDATE_SUCCESS, + // isActive: category.isActive, + // }; + // } + + // //******************************** */ + + // private async checkTicketLimit(userId: string, queryRunner: QueryRunner): Promise { + // const { userSupportPlan } = await this.supportPlansService.getUserSupportPlanOfUser(userId); + + // if (!userSupportPlan) throw new BadRequestException(TicketMessageEnum.NO_ACTIVE_SUPPORT_PLAN); + + // const ticketLimitFeature = userSupportPlan.supportPlan.features.find( + // (feature) => feature.featureKey === SupportPlanFeatureKey.TICKET_LIMIT, + // ); + + // if (!ticketLimitFeature) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_NOT_FOUND); + + // const ticketLimit = parseInt(ticketLimitFeature.featureValue); + // if (isNaN(ticketLimit)) return; + + // const today = dayjs().startOf("day").toDate(); + + // const ticketCount = await queryRunner.manager.count(this.ticketsRepository.target, { + // where: { + // user: { id: userId }, + // createdAt: MoreThanOrEqual(today), + // }, + // }); + + // if (ticketCount >= ticketLimit) throw new BadRequestException(TicketMessageEnum.TICKET_LIMIT_EXCEEDED); + // } + + // //******************************** */ + + // async createTicket(createDto: CreateTicketDto, userId: string) { + // const queryRunner = this.dataSource.createQueryRunner(); + + // try { + // await queryRunner.connect(); + // await queryRunner.startTransaction(); + + // const [user, ticketCategory] = await Promise.all([ + // this.usersService.findOneByIdWithQueryRunner(userId, queryRunner), + // queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }), + // ]); + + // if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); + + // await this.checkTicketLimit(userId, queryRunner); + + // let service = null; + // if (createDto.danakServiceId) { + // service = await queryRunner.manager.findOneBy(DanakService, { id: createDto.danakServiceId }); + // if (!service) throw new BadRequestException(TicketMessageEnum.SERVICE_NOT_FOUND); + // } + + // const ticketMessage: Partial = { content: createDto.message, author: user }; + + // if (createDto.attachmentUrls?.length) { + // ticketMessage.attachments = createDto.attachmentUrls.map((url) => { + // const attachment = new TicketMessageAttachment(); + // attachment.attachmentUrl = url; + // return attachment; + // }); + // } + + // const ticket = queryRunner.manager.create(Ticket, { + // ...createDto, + // user, + // category: ticketCategory, + // messages: [ticketMessage], + // danakService: service, + // }); + + // await queryRunner.manager.save(Ticket, ticket); + + // await this.assignTicketAndNotify(ticket, ticketCategory, user, queryRunner); + + // await queryRunner.commitTransaction(); + + // return { + // message: TicketMessageEnum.CREATED, + // ticketId: ticket.id, + // }; + // } catch (error) { + // this.logger.error("Error in create ticket transaction", error); + // await queryRunner.rollbackTransaction(); + // throw error; + // } finally { + // await queryRunner.release(); + // } + // } + // //******************************** */ + + // async createTicketByAdmin(createDto: CreateTicketByAdminDto, userIpAndHeaders: IUserIpAndHeaders) { + // const queryRunner = this.dataSource.createQueryRunner(); + + // try { + // await queryRunner.connect(); + // await queryRunner.startTransaction(); + + // const [user, admin, ticketCategory] = await Promise.all([ + // this.usersService.findOneByIdWithQueryRunner(createDto.userId, queryRunner), + // this.usersService.findOneByIdWithQueryRunner(userIpAndHeaders.userId, queryRunner), + // queryRunner.manager.findOne(TicketCategory, { where: { id: createDto.categoryId }, relations: { group: { users: true } } }), + // ]); + + // if (!ticketCategory) throw new BadRequestException(TicketMessageEnum.CATEGORY_NOT_FOUND); + + // const ticketMessage: Partial = { content: createDto.message, author: admin }; + + // const ticket = queryRunner.manager.create(Ticket, { + // ...createDto, + // user, + // category: ticketCategory, + // messages: [ticketMessage], + // assignedTo: admin, + // }); + + // await queryRunner.manager.save(Ticket, ticket); + + // await this.notificationQueue.addTicketNotification(user.id, { + // ticketId: ticket.numericId.toString(), + // subject: ticket.subject, + // date: ticket.createdAt, + // userPhone: user.phone, + // userEmail: user.email, + // }); + + // await this.accessLogService.logCreate("Ticket", UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin created ticket: ${ticket.subject}`, + // metadata: { + // ticketId: ticket.id, + // ticketSubject: ticket.subject, + // }, + // }); + + // await queryRunner.commitTransaction(); + + // return { + // message: TicketMessageEnum.CREATED, + // ticketId: ticket.id, + // }; + // } catch (error) { + // await queryRunner.rollbackTransaction(); + // throw error; + // } finally { + // await queryRunner.release(); + // } + // } + + // //******************************** */ + + // async getTickets(queryDto: SearchTicketQueryDto, userId: string) { + // const [tickets, count] = await this.ticketsRepository.findTicketsList(queryDto, userId); + + // return { tickets, count, paginate: true }; + // } + // //******************************** */ + // async getTicketForAdmin(queryDto: SearchTicketQueryDto, adminId: string) { + // const isSuperAdmin = await this.usersService.isSuperAdmin(adminId); + + // const [tickets, count] = await this.ticketsRepository.findTicketsListForAdmin(queryDto, adminId, isSuperAdmin); + + // return { tickets, count, paginate: true }; + // } + + // //******************************** */ + + // async createTicketMessage(ticketId: string, createDto: CreateTicketMessageDto, userId: string, isAdmin: boolean) { + // const queryRunner = this.dataSource.createQueryRunner(); + // await queryRunner.connect(); + // await queryRunner.startTransaction(); + + // try { + // const ticket = await this.findTicket(queryRunner, ticketId, userId, isAdmin); + // if (ticket.status === TicketStatus.CLOSED) throw new BadRequestException(TicketMessageEnum.TICKET_CLOSED); + + // const isSuperAdmin = await this.usersService.isSuperAdmin(userId); + // if (isAdmin && !isSuperAdmin && ticket.assignedTo?.id !== userId) + // throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER); + + // const user = await queryRunner.manager.findOneBy(User, { id: userId }); + // if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND); + + // const ticketMessage = queryRunner.manager.create(TicketMessage, { + // content: createDto.content, + // author: user, + // ticket, + // }); + // await queryRunner.manager.save(TicketMessage, ticketMessage); + + // if (createDto.attachmentUrls?.length) { + // await this.saveAttachments(queryRunner, createDto.attachmentUrls, ticketMessage); + // } + + // this.updateTicketStatus(ticket, isAdmin); + // await queryRunner.manager.save(Ticket, ticket); + + // if (isAdmin) { + // await this.sendNotificationForAdminResponse(ticket, user); + // } + + // await queryRunner.commitTransaction(); + // return { + // message: TicketMessageEnum.MESSAGE_CREATED, + // ticketMessage, + // }; + // } catch (error) { + // await queryRunner.rollbackTransaction(); + // if (error instanceof HttpException) throw error; + // throw new InternalServerErrorException(TicketMessageEnum.ERROR_IN_TICKET_MSG_CREATION); + // } finally { + // await queryRunner.release(); + // } + // } + + // //******************************** */ + + // async getTicketMessages(ticketId: string, userId: string, isAdmin: boolean) { + // let ticket: null | Ticket = null; + // // + // if (isAdmin) { + // ticket = await this.ticketsRepository.findTicketById(ticketId); + // } else { + // ticket = await this.ticketsRepository.findTicketById(ticketId, userId); + // } + // if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); + + // const messages = await this.ticketMessagesRepository.findMessagesByTicketId(ticketId); + // const userSupportPlan = await this.supportPlansService.getUserSupportPlanOfUser(ticket.user.id); + // return { + // ticket, + // messages, + // supportPlan: userSupportPlan, + // }; + // } + + // //******************************** */ + + // async closeTicketByUser(ticketId: string, isAdmin: boolean, userIpAndHeaders: IUserIpAndHeaders) { + // let ticket: null | Ticket = null; + + // if (isAdmin) { + // ticket = await this.ticketsRepository.findTicketById(ticketId); + // } else { + // ticket = await this.ticketsRepository.findTicketById(ticketId, userIpAndHeaders.userId); + // } + + // if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); + + // ticket.status = TicketStatus.CLOSED; + // await this.ticketsRepository.save(ticket); + + // await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin closed ticket: ${ticket.subject}`, + // metadata: { + // ticketId: ticket.id, + // ticketSubject: ticket.subject, + // }, + // }); + + // return { + // message: TicketMessageEnum.CLOSED, + // }; + // } + // //******************************** * / + // async openTicketByAdmin(ticketId: string, userIpAndHeaders: IUserIpAndHeaders) { + // const ticket = await this.ticketsRepository.findTicketById(ticketId); + // if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); + + // ticket.status = TicketStatus.PENDING; + // await this.ticketsRepository.save(ticket); + + // await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin opened ticket: ${ticket.subject}`, + // metadata: { + // ticketId: ticket.id, + // ticketSubject: ticket.subject, + // }, + // }); + + // return { + // message: TicketMessageEnum.OPENED, + // }; + // } + + // //******************************** */ + // async referTicket(ticketId: string, referDto: ReferTicketDto, userIpAndHeaders: IUserIpAndHeaders) { + // const queryRunner = this.dataSource.createQueryRunner(); + + // try { + // await queryRunner.connect(); + // await queryRunner.startTransaction(); + + // const isSuperAdmin = await this.usersService.isSuperAdmin(userIpAndHeaders.userId); + // // if (!isSuperAdmin) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_ASSIGNED_TO_USER); + // console.log("isSuperAdmin", isSuperAdmin); + + // const ticket = await this.ticketsRepository.findTicketById(ticketId); + // if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); + + // const admin = await this.usersService.findOneByIdWithQueryRunner(referDto.adminId, queryRunner); + + // ticket.assignedTo = admin; + // await queryRunner.manager.save(this.ticketsRepository.target, ticket); + + // await this.notificationQueue.addAssignTicketNotificationForAdmin(admin.id, { + // ticketId: ticket.numericId.toString(), + // subject: ticket.subject, + // date: ticket.createdAt, + // userPhone: admin.phone, + // userEmail: admin.email, + // user: `${ticket.user.firstName} ${ticket.user.lastName}`, + // }); + + // await queryRunner.commitTransaction(); + + // await this.accessLogService.logUpdate("Ticket", ticketId, UserType.ADMIN, { + // user: { id: userIpAndHeaders.userId }, + // endpoint: "/tickets", + // requestId: userIpAndHeaders.headers["x-request-id"], + // ipAddress: userIpAndHeaders.ip, + // userAgent: userIpAndHeaders.headers["user-agent"], + // actionDescription: `Admin referred ticket: ${ticket.subject}`, + // metadata: { + // ticketId: ticket.id, + // ticketSubject: ticket.subject, + // }, + // }); + // return { + // message: TicketMessageEnum.REFERRED, + // ticket, + // }; + // } catch (error) { + // await queryRunner.rollbackTransaction(); + // throw error; + // } finally { + // await queryRunner.release(); + // } + // } + + // //******************************** */ + + // async getUnreadTickets() { + // const unreadTickets = await this.ticketsRepository.count({ + // where: { status: TicketStatus.PENDING }, + // }); + // return unreadTickets; + // } + + // //******************************** */ + // async countUserTickets(userId: string) { + // const ticketCount = await this.ticketsRepository.count({ + // where: { user: { id: userId }, status: Not(TicketStatus.CLOSED) }, + // }); + // return ticketCount; + // } + + // //***************************** */ + // private async assignTicketAndNotify(ticket: Ticket, ticketCategory: TicketCategory, user: User, queryRunner: QueryRunner) { + // const assignedUser = await this.referralService.assignAdmin(ticketCategory.group); + // // + // ticket.assignedTo = assignedUser; + // await queryRunner.manager.save(Ticket, ticket); + + // await this.notificationQueue.addAssignTicketNotificationForAdmin(assignedUser.id, { + // ticketId: ticket.numericId.toString(), + // subject: ticket.subject, + // date: ticket.createdAt, + // userPhone: user.phone, + // userEmail: user.email, + // user: `${user.firstName} ${user.lastName}`, + // }); + + // await this.notificationQueue.addTicketNotification(user.id, { + // ticketId: ticket.numericId.toString(), + // subject: ticket.subject, + // date: ticket.createdAt, + // userPhone: user.phone, + // userEmail: user.email, + // }); + // await this.notifySuperAdminsForTicket(queryRunner, ticket); + // } + // //***************************** */ + + // private async findTicket(queryRunner: QueryRunner, ticketId: string, userId: string, isAdmin: boolean): Promise { + // const ticket = isAdmin + // ? await queryRunner.manager.findOne(Ticket, { + // where: { id: ticketId }, + // relations: { user: true, assignedTo: true }, + // }) + // : await queryRunner.manager.findOne(Ticket, { + // where: { id: ticketId, user: { id: userId } }, + // relations: { user: true, assignedTo: true }, + // }); + + // if (!ticket) throw new BadRequestException(TicketMessageEnum.TICKET_NOT_FOUND); + // return ticket; + // } + // //***************************** */ + + // private updateTicketStatus(ticket: Ticket, isAdmin: boolean): void { + // if (isAdmin && ticket.status === TicketStatus.PENDING) { + // ticket.status = TicketStatus.ANSWERED; + // } else if (!isAdmin && ticket.status === TicketStatus.ANSWERED) { + // ticket.status = TicketStatus.PENDING; + // } + // } + // //***************************** */ + + // private async saveAttachments(queryRunner: QueryRunner, attachmentUrls: string[], ticketMessage: TicketMessage) { + // const attachments = attachmentUrls.map((url) => + // queryRunner.manager.create(TicketMessageAttachment, { + // attachmentUrl: url, + // ticketMessage, + // }), + // ); + // await queryRunner.manager.save(TicketMessageAttachment, attachments); + // } + // //***************************** */ + + // private async sendNotificationForAdminResponse(ticket: Ticket, admin: User) { + // await this.notificationQueue.addAnswerTicketNotification(ticket.user.id, { + // ticketId: ticket.numericId.toString(), + // subject: ticket.subject, + // date: ticket.createdAt, + // userPhone: admin.phone, + // userEmail: admin.email, + // }); + // } + // //***************************** */ + + // private async notifySuperAdminsForTicket(queryRunner: QueryRunner, ticket: Ticket) { + // const superAdmins = await this.adminsService.getSuperAdmins(queryRunner); + + // for (const admin of superAdmins) { + // await this.notificationQueue.addNewTicketGlobalNotification(admin.id, { + // ticketId: ticket.numericId.toString(), + // ticketSubject: ticket.subject, + // userPhone: admin.phone, + // userEmail: admin.email, + // fullName: `${ticket.user.firstName} ${ticket.user.lastName}`, + // }); + // } + // } + // //***************************** */ + + // async scheduleAutoCloseJob() { + // await this.ticketQueue.add( + // TICKET.AUTO_CLOSE_JOB_NAME, + // {}, + // { + // repeat: { pattern: TICKET.AUTO_CLOSE_REPEAT_PATTERN }, + // priority: TICKET.AUTO_CLOSE_PRIORITY, + // }, + // ); + // this.logger.log("Scheduled auto-close-inactive job for tickets"); + // } } diff --git a/src/modules/ticket/repositories/tickets-message.repository.ts b/src/modules/ticket/repositories/tickets-message.repository.ts deleted file mode 100755 index b39f23f..0000000 --- a/src/modules/ticket/repositories/tickets-message.repository.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Injectable } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; - -import { TicketMessage } from "../entities/ticket-message.entity"; - -@Injectable() -export class TicketMessagesRepository extends Repository { - constructor(@InjectRepository(TicketMessage) ticketMessagesRepository: Repository) { - super(ticketMessagesRepository.target, ticketMessagesRepository.manager, ticketMessagesRepository.queryRunner); - } - - async findMessagesByTicketId(ticketId: string): Promise { - return this.find({ - relations: { - author: { - roles: true, - }, - attachments: true, - // ticket: true, - }, - select: { - id: true, - content: true, - attachments: { - id: true, - attachmentUrl: true, - }, - createdAt: true, - author: { - id: true, - firstName: true, - lastName: true, - roles: { - id: true, - name: true, - }, - }, - }, - where: { ticket: { id: ticketId } }, - order: { createdAt: "ASC" }, - }); - } -} diff --git a/src/modules/ticket/repositories/tickets.repository.ts b/src/modules/ticket/repositories/tickets.repository.ts index 5e6b641..76c7ab1 100755 --- a/src/modules/ticket/repositories/tickets.repository.ts +++ b/src/modules/ticket/repositories/tickets.repository.ts @@ -1,119 +1,13 @@ -import { Injectable } from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; - -import { PaginationUtils } from "../../utils/providers/pagination.utils"; -import { SearchTicketQueryDto } from "../DTO/search-ticket-query.dto"; -import { Ticket } from "../entities/ticket.entity"; -import { TicketStatus } from "../enums/ticket-status.enum"; +import { EntityManager, EntityRepository } from '@mikro-orm/postgresql'; + import { Injectable } from '@nestjs/common'; +import { Ticket } from '../entities/ticket.entity'; @Injectable() -export class TicketsRepository extends Repository { - constructor(@InjectRepository(Ticket) ticketsRepository: Repository) { - super(ticketsRepository.target, ticketsRepository.manager, ticketsRepository.queryRunner); +export class TicketRepository extends EntityRepository { + constructor( + readonly em: EntityManager, + ) { + super(em, Ticket); } - async findTicketById(id: string, userId?: string): Promise { - return this.findOne({ - where: { id, ...(userId && { user: { id: userId } }) }, - relations: { - user: true, - category: true, - assignedTo: true, - danakService: true, - }, - select: { - id: true, - numericId: true, - danakService: { - id: true, - name: true, - }, - subject: true, - priority: true, - status: true, - createdAt: true, - user: { - id: true, - firstName: true, - lastName: true, - }, - assignedTo: { - id: true, - firstName: true, - lastName: true, - }, - category: { - id: true, - title: true, - }, - }, - }); - } - - //******************************** */ - - async findTicketsList(queryDto: SearchTicketQueryDto, userId: string) { - const { limit, skip } = PaginationUtils(queryDto); - - const queryBuilder = this.createQueryBuilder("ticket") - .leftJoinAndSelect("ticket.category", "category") - .leftJoinAndSelect("ticket.danakService", "danakService") - .where("ticket.user.id = :userId", { userId }) - .orderBy("ticket.createdAt", "DESC") - .select([ - "ticket.id", - "ticket.subject", - "ticket.status", - "ticket.priority", - "ticket.numericId", - "ticket.createdAt", - "ticket.updatedAt", - "category.id", - "category.title", - "danakService.id", - "danakService.name", - ]) - .skip(skip) - .take(limit); - - if (queryDto.status) { - queryBuilder.andWhere("ticket.status = :status", { - status: queryDto.status, - }); - } else { - queryBuilder.andWhere("ticket.status != :closedStatus", { - closedStatus: TicketStatus.CLOSED, - }); - } - - return queryBuilder.getManyAndCount(); - } - - async findTicketsListForAdmin(queryDto: SearchTicketQueryDto, adminId: string, isSuperAdmin: boolean) { - const { limit, skip } = PaginationUtils(queryDto); - - const queryBuilder = this.createQueryBuilder("ticket") - .leftJoinAndSelect("ticket.category", "category") - .leftJoinAndSelect("ticket.user", "user") - .leftJoinAndSelect("ticket.assignedTo", "assignedTo") - .leftJoinAndSelect("ticket.danakService", "danakService") - .orderBy("ticket.createdAt", "DESC"); - - if (!isSuperAdmin) { - queryBuilder.andWhere("ticket.assignedToId = :adminId", { adminId }); - } - - if (queryDto.status) { - queryBuilder.andWhere("ticket.status = :status", { - status: queryDto.status, - }); - } - - if (queryDto.userId) { - queryBuilder.andWhere("user.id = :userId", { userId: queryDto.userId }); - } - - return queryBuilder.skip(skip).take(limit).getManyAndCount(); - } } diff --git a/src/modules/ticket/tickets.controller.ts b/src/modules/ticket/tickets.controller.ts index 5b6a50a..1311385 100755 --- a/src/modules/ticket/tickets.controller.ts +++ b/src/modules/ticket/tickets.controller.ts @@ -4,137 +4,133 @@ import { ApiOperation, ApiTags } from "@nestjs/swagger"; import { CreateTicketCategoryDto } from "./DTO/create-ticket-category.dto"; import { CreateTicketMessageDto } from "./DTO/create-ticket-message.dto"; import { CreateTicketByAdminDto, CreateTicketDto } from "./DTO/create-ticket.dto"; -import { ReferTicketDto } from "./DTO/refer-ticket.dto"; -import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto"; + import { SearchTicketCategoryDto } from "./DTO/search-ticket-category.dto"; import { SearchTicketQueryDto } from "./DTO/search-ticket-query.dto"; -import { UpdateTicketCategoryDto } from "./DTO/update-ticket-category.dto"; -import { TicketsService } from "./providers/tickets.service"; -import { AdminRoute } from "../../common/decorators/admin.decorator"; -import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; -import { PermissionsDec } from "../../common/decorators/permission.decorator"; -import { IUserIpAndHeaders, UserDec, UserIpAndHeaders } from "../../common/decorators/user.decorator"; + import { TicketsService } from "./providers/tickets.service"; + + import { ParamDto } from "../../common/DTO/param.dto"; -import { PermissionEnum } from "../users/enums/permission.enum"; + @Controller("tickets") @ApiTags("Tickets") -@AuthGuards() + export class TicketsController { constructor(private readonly ticketsService: TicketsService) {} //----------------- ticket categories ------------------- - @ApiOperation({ summary: "Create ticket category => admin route" }) - @PermissionsDec(PermissionEnum.TICKETS) - @Post("category") - createTicketCategory(@Body() createDto: CreateTicketCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { - return this.ticketsService.createTicketCategory(createDto, userIpAndHeaders); - } + // @ApiOperation({ summary: "Create ticket category => admin route" }) + // @PermissionsDec(PermissionEnum.TICKETS) + // @Post("category") + // createTicketCategory(@Body() createDto: CreateTicketCategoryDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + // return this.ticketsService.createTicketCategory(createDto, userIpAndHeaders); + // } - @ApiOperation({ summary: "Update ticket category => admin route" }) - @PermissionsDec(PermissionEnum.TICKETS) - @Patch("category/:id") - updateCategory( - @Param() paramDto: ParamDto, - @Body() updateCategoryDto: UpdateTicketCategoryDto, - @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders, - ) { - return this.ticketsService.updateCategory(paramDto, updateCategoryDto, userIpAndHeaders); - } + // @ApiOperation({ summary: "Update ticket category => admin route" }) + // @PermissionsDec(PermissionEnum.TICKETS) + // @Patch("category/:id") + // updateCategory( + // @Param() paramDto: ParamDto, + // @Body() updateCategoryDto: UpdateTicketCategoryDto, + // @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders, + // ) { + // return this.ticketsService.updateCategory(paramDto, updateCategoryDto, userIpAndHeaders); + // } - @ApiOperation({ summary: "Get ticket categories " }) - @Get("categories") - getTicketCategories() { - return this.ticketsService.getTicketCategories(); - } + // @ApiOperation({ summary: "Get ticket categories " }) + // @Get("categories") + // getTicketCategories() { + // return this.ticketsService.getTicketCategories(); + // } - @ApiOperation({ summary: "Get ticket categories list => admin route" }) - @AdminRoute() - @PermissionsDec(PermissionEnum.TICKETS) - @Get("categories-list") - getCategoriesList(@Query() queryDto: SearchTicketCategoryDto) { - return this.ticketsService.getCategoriesList(queryDto); - } + // @ApiOperation({ summary: "Get ticket categories list => admin route" }) + // @AdminRoute() + // @PermissionsDec(PermissionEnum.TICKETS) + // @Get("categories-list") + // getCategoriesList(@Query() queryDto: SearchTicketCategoryDto) { + // return this.ticketsService.getCategoriesList(queryDto); + // } - @ApiOperation({ summary: "toggle status of categories => admin route" }) - @AdminRoute() - @HttpCode(HttpStatus.OK) - @PermissionsDec(PermissionEnum.TICKETS) - @Post("category/toggle-status/:id") - toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { - return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders); - } + // @ApiOperation({ summary: "toggle status of categories => admin route" }) + // @AdminRoute() + // @HttpCode(HttpStatus.OK) + // @PermissionsDec(PermissionEnum.TICKETS) + // @Post("category/toggle-status/:id") + // toggleCategoryStatus(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + // return this.ticketsService.toggleCategoryStatus(paramDto, userIpAndHeaders); + // } //----------------- ticket and ticket messages ------------------- - @ApiOperation({ summary: "create ticket ==> user route" }) - @Post() - createTicket(@Body() createDto: CreateTicketDto, @UserDec("id") userId: string) { - return this.ticketsService.createTicket(createDto, userId); - } + // @ApiOperation({ summary: "create ticket ==> user route" }) + // @Post() + // createTicket(@Body() createDto: CreateTicketDto, @UserDec("id") userId: string) { + // return this.ticketsService.createTicket(createDto, userId); + // } - @ApiOperation({ summary: "create ticket by admin" }) - @AdminRoute() - @PermissionsDec(PermissionEnum.TICKETS) - @Post("admin") - createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { - return this.ticketsService.createTicketByAdmin(createDto, userIpAndHeaders); - } + // @ApiOperation({ summary: "create ticket by admin" }) + // @AdminRoute() + // @PermissionsDec(PermissionEnum.TICKETS) + // @Post("admin") + // createTicketByAdmin(@Body() createDto: CreateTicketByAdminDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + // return this.ticketsService.createTicketByAdmin(createDto, userIpAndHeaders); + // } - @ApiOperation({ summary: "Get all tickets of user ==> user route" }) - @Get() - getTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") userId: string) { - return this.ticketsService.getTickets(queryDto, userId); - } + // @ApiOperation({ summary: "Get all tickets of user ==> user route" }) + // @Get() + // getTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") userId: string) { + // return this.ticketsService.getTickets(queryDto, userId); + // } - @ApiOperation({ summary: "Get all tickets ==> admin route" }) - @AdminRoute() - @PermissionsDec(PermissionEnum.TICKETS) - @Get("admin") - getAllTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") adminId: string) { - return this.ticketsService.getTicketForAdmin(queryDto, adminId); - } + // @ApiOperation({ summary: "Get all tickets ==> admin route" }) + // @AdminRoute() + // @PermissionsDec(PermissionEnum.TICKETS) + // @Get("admin") + // getAllTickets(@Query() queryDto: SearchTicketQueryDto, @UserDec("id") adminId: string) { + // return this.ticketsService.getTicketForAdmin(queryDto, adminId); + // } - @ApiOperation({ summary: "create ticket messages" }) - @Post(":id/messages") - createTicketMessage( - @Param() paramDto: ParamDto, - @Body() createDto: CreateTicketMessageDto, - @UserDec("id") userId: string, - @UserDec("isAdmin") isAdmin: boolean, - ) { - return this.ticketsService.createTicketMessage(paramDto.id, createDto, userId, isAdmin); - } + // @ApiOperation({ summary: "create ticket messages" }) + // @Post(":id/messages") + // createTicketMessage( + // @Param() paramDto: ParamDto, + // @Body() createDto: CreateTicketMessageDto, + // @UserDec("id") userId: string, + // @UserDec("isAdmin") isAdmin: boolean, + // ) { + // return this.ticketsService.createTicketMessage(paramDto.id, createDto, userId, isAdmin); + // } - @ApiOperation({ summary: "Get all ticket messages of user" }) - @Get(":id/messages") - getTicketMessages(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) { - return this.ticketsService.getTicketMessages(paramDto.id, userId, isAdmin); - } + // @ApiOperation({ summary: "Get all ticket messages of user" }) + // @Get(":id/messages") + // getTicketMessages(@Param() paramDto: ParamDto, @UserDec("id") userId: string, @UserDec("isAdmin") isAdmin: boolean) { + // return this.ticketsService.getTicketMessages(paramDto.id, userId, isAdmin); + // } - @ApiOperation({ summary: "close ticket by user or admin" }) - @HttpCode(HttpStatus.OK) - @Post(":id/close") - closedTicketByUser( - @Param() paramDto: ParamDto, - @UserDec("isAdmin") isAdmin: boolean, - @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders, - ) { - return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders); - } + // @ApiOperation({ summary: "close ticket by user or admin" }) + // @HttpCode(HttpStatus.OK) + // @Post(":id/close") + // closedTicketByUser( + // @Param() paramDto: ParamDto, + // @UserDec("isAdmin") isAdmin: boolean, + // @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders, + // ) { + // return this.ticketsService.closeTicketByUser(paramDto.id, isAdmin, userIpAndHeaders); + // } - @ApiOperation({ summary: "open ticket by admin" }) - @AdminRoute() - @PermissionsDec(PermissionEnum.TICKETS) - @Post(":id/open") - openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { - return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders); - } + // @ApiOperation({ summary: "open ticket by admin" }) + // @AdminRoute() + // @PermissionsDec(PermissionEnum.TICKETS) + // @Post(":id/open") + // openTicketByAdmin(@Param() paramDto: ParamDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + // return this.ticketsService.openTicketByAdmin(paramDto.id, userIpAndHeaders); + // } - @ApiOperation({ summary: "referer ticket ==> admin route" }) - @AdminRoute() - @PermissionsDec(PermissionEnum.TICKETS) - @Post(":id/refer") - referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { - return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders); - } + // @ApiOperation({ summary: "referer ticket ==> admin route" }) + // @AdminRoute() + // @PermissionsDec(PermissionEnum.TICKETS) + // @Post(":id/refer") + // referTicket(@Param() paramDto: ParamDto, @Body() referDto: ReferTicketDto, @UserIpAndHeaders() userIpAndHeaders: IUserIpAndHeaders) { + // return this.ticketsService.referTicket(paramDto.id, referDto, userIpAndHeaders); + // } } diff --git a/src/modules/ticket/tickets.module.ts b/src/modules/ticket/tickets.module.ts index e9db3af..68b7ad3 100755 --- a/src/modules/ticket/tickets.module.ts +++ b/src/modules/ticket/tickets.module.ts @@ -6,8 +6,7 @@ import { Ticket } from "./entities/ticket.entity"; import { TicketsService } from "./providers/tickets.service"; -import { TicketMessagesRepository } from "./repositories/tickets-message.repository"; -import { TicketsRepository } from "./repositories/tickets.repository"; + import { TicketRepository } from "./repositories/tickets.repository"; import { TicketsController } from "./tickets.controller"; import { MikroOrmModule } from "@mikro-orm/nestjs"; @@ -18,7 +17,7 @@ import { MikroOrmModule } from "@mikro-orm/nestjs"; MikroOrmModule.forFeature([Ticket]), BullModule.registerQueue({ name: TICKET.QUEUE_NAME }), ], - providers: [TicketsService, TicketsRepository, TicketMessagesRepository], + providers: [TicketsService, TicketRepository], controllers: [TicketsController], exports: [TicketsService], }) diff --git a/src/modules/user/dto/find-wallet-transactions.dto.ts b/src/modules/user/dto/find-wallet-transactions.dto.ts index 2aa5d4b..c093fc1 100644 --- a/src/modules/user/dto/find-wallet-transactions.dto.ts +++ b/src/modules/user/dto/find-wallet-transactions.dto.ts @@ -1,8 +1,7 @@ import { IsOptional, IsString, IsNumber, Min, IsIn, IsDateString } from 'class-validator'; import { Type } from 'class-transformer'; import { ApiPropertyOptional } from '@nestjs/swagger'; -import { WalletTransactionType, WalletTransactionReason } from '../interface/wallet'; - + // Define the valid sort directions const sortOrderOptions = ['asc', 'desc'] as const; type SortOrder = (typeof sortOrderOptions)[number]; @@ -40,27 +39,12 @@ export class FindWalletTransactionsDto { @Type(() => Number) limit?: number = 10; - /** - * Filter by transaction type - */ - @ApiPropertyOptional({ - description: 'Transaction type filter', - enum: WalletTransactionType, - }) - @IsOptional() - @IsIn(Object.values(WalletTransactionType)) - type?: WalletTransactionType; + /** * Filter by transaction reason */ - @ApiPropertyOptional({ - description: 'Transaction reason filter', - enum: WalletTransactionReason, - }) - @IsOptional() - @IsIn(Object.values(WalletTransactionReason)) - reason?: WalletTransactionReason; + /** * Filter transactions from this date onwards diff --git a/src/seeders/users.seeder.ts b/src/seeders/users.seeder.ts index 0a0ccf8..d0bfddb 100644 --- a/src/seeders/users.seeder.ts +++ b/src/seeders/users.seeder.ts @@ -18,10 +18,9 @@ export class UsersSeeder { phone: normalizedPhone, // Use normalized phone firstName: userData.firstName, lastName: userData.lastName, - birthDate: new Date(userData.birthDate), - marriageDate: new Date(userData.marriageDate), isActive: userData.isActive, gender: userData.gender, + maxCredit: 0 }); em.persist(user); }