diff --git a/src/modules/ticket/DTO/create-ticket.dto.ts b/src/modules/ticket/DTO/create-ticket.dto.ts index f3cb143..b0e4bea 100755 --- a/src/modules/ticket/DTO/create-ticket.dto.ts +++ b/src/modules/ticket/DTO/create-ticket.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from "@nestjs/swagger"; -import { ArrayMinSize, IsEnum, IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; +import { IsNotEmpty, IsOptional, IsString, IsUUID, IsUrl, Length } from "class-validator"; -import { TicketMessageEnum, UserMessage } from "../../../common/enums/message.enum"; +import { TicketMessageEnum } from "../../../common/enums/message.enum"; export class CreateTicketDto { diff --git a/src/modules/ticket/DTO/search-ticket-category.dto.ts b/src/modules/ticket/DTO/search-ticket-category.dto.ts deleted file mode 100755 index f1af126..0000000 --- a/src/modules/ticket/DTO/search-ticket-category.dto.ts +++ /dev/null @@ -1,3 +0,0 @@ - - -export class SearchTicketCategoryDto {} diff --git a/src/modules/ticket/DTO/update-ticket.dto.ts b/src/modules/ticket/DTO/update-ticket.dto.ts new file mode 100755 index 0000000..25261e7 --- /dev/null +++ b/src/modules/ticket/DTO/update-ticket.dto.ts @@ -0,0 +1,6 @@ +import { PartialType } from "@nestjs/swagger"; +import { CreateTicketDto } from "./create-ticket.dto"; + + +export class UpdateTicketDto extends PartialType(CreateTicketDto){} + diff --git a/src/modules/ticket/providers/tickets.service.ts b/src/modules/ticket/providers/tickets.service.ts index 8fb0d96..6274eba 100755 --- a/src/modules/ticket/providers/tickets.service.ts +++ b/src/modules/ticket/providers/tickets.service.ts @@ -10,6 +10,8 @@ import { UserRepository } from "src/modules/user/repositories/user.repository"; import { AdminRepository } from "src/modules/admin/repositories/admin.repository"; import { Admin } from "src/modules/admin/entities/admin.entity"; import { FindTicketQueryDto } from "../DTO/search-ticket-query.dto"; +import { EntityManager } from "@mikro-orm/postgresql"; +import { UpdateTicketDto } from "../DTO/update-ticket.dto"; @Injectable() @@ -21,7 +23,7 @@ export class TicketService { private readonly orderRepository: OrderRepository, private readonly userRepository: UserRepository, private readonly adminRepository: AdminRepository, - // private readonly adminsService: AdminsService, + private readonly em: EntityManager, ) { } async createTicket(orderId: string, dto: CreateTicketDto, userId?: string, adminId?: string) { @@ -67,271 +69,6 @@ export class TicketService { return ticket } - // 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(); - // } - // } // //******************************** */ @@ -341,7 +78,7 @@ export class TicketService { if (!order) { throw new BadRequestException("order not found!") } - if(userId){ + if (userId) { if (order.user.id !== userId) { throw new BadRequestException("This ticket doenst belongs to You !") } @@ -350,8 +87,41 @@ export class TicketService { return data } + //******************************** */ + async deleteTicket(ticketId: string, userId?: string) { + const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] }) + if (!ticket) { + throw new BadRequestException('ticket not found!') + } + if (userId && ticket.order.user.id !== userId) { + throw new BadRequestException("You can not Delete this Ticket") + } + + await this.em.removeAndFlush(ticket) + + return true + } + + // //******************************** */ + + async updateTicket(ticketId: string, dto: UpdateTicketDto, userId?: string) { + const ticket = await this.ticketsRepository.findOne({ id: ticketId }, { populate: ['order', 'order.user'] }) + if (!ticket) { + throw new BadRequestException('ticket not found!') + } + if (userId && ticket.order.user.id !== userId) { + throw new BadRequestException("You can not Update this Ticket") + } + this.ticketsRepository.assign(ticket, dto, { + onlyProperties: true, + }) + + await this.em.flush() + + return true + } // //******************************** */ diff --git a/src/modules/ticket/tickets.controller.ts b/src/modules/ticket/tickets.controller.ts index 33c489e..dd1bedc 100755 --- a/src/modules/ticket/tickets.controller.ts +++ b/src/modules/ticket/tickets.controller.ts @@ -1,13 +1,11 @@ -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 { CreateTicketDto } from "./DTO/create-ticket.dto"; import { FindTicketQueryDto } from "./DTO/search-ticket-query.dto"; import { TicketService } from "./providers/tickets.service"; - - -import { ParamDto } from "../../common/DTO/param.dto"; import { UserId } from "src/common/decorators"; import { AdminId } from "src/common/decorators/admin-id.decorator"; +import { UpdateTicketDto } from "./DTO/update-ticket.dto"; @Controller("tickets") @ApiTags("Tickets") @@ -73,8 +71,26 @@ export class TicketsController { return this.ticketsService.getTickets(orderId, queryDto, userId); } + @ApiOperation({ summary: "Delete Ticket ==> admin route" }) + @Delete('public/ticket/:ticketId') + deleteTicket(@Param('ticketId') ticketId: string, @UserId() userId: string) { + return this.ticketsService.deleteTicket(ticketId, userId); + } + + @ApiOperation({ summary: "Delete Ticket ==> admin route" }) + @Patch('public/ticket/:ticketId') + updateTicket(@Param('ticketId') ticketId: string, @UserId() userId: string, @Body() body: UpdateTicketDto) { + return this.ticketsService.updateTicket(ticketId, body, userId,); + } + /*=========================== Admin ============================== */ + @ApiOperation({ summary: "Delete Ticket ==> admin route" }) + @Delete('admin/ticket/:ticketId') + deleteTicketAsAdmin(@Param('ticketId') ticketId: string) { + return this.ticketsService.deleteTicket(ticketId); + } + @Post('admin/ticket/order/:orderId') @ApiOperation({ summary: "create ticket ==> admin route" }) createTicketAsAdmin(@Param('orderId') orderId: string, @Body() createDto: CreateTicketDto, @AdminId() adminId: string) { @@ -87,55 +103,10 @@ export class TicketsController { getTicketsAsAdmin(@Query() queryDto: FindTicketQueryDto, @Param('orderId') orderId: string) { return this.ticketsService.getTickets(orderId, queryDto); } - // @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: "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: "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: "Delete Ticket ==> admin route" }) + @Patch('admin/ticket/:ticketId') + updateTicketAsAdmin(@Param('ticketId') ticketId: string, @Body() body: UpdateTicketDto) { + return this.ticketsService.updateTicket(ticketId, body); + } }