From 37d3a241e7dadfb35c65c3c61b9206fdb8312fe2 Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Tue, 24 Feb 2026 16:34:28 +0330 Subject: [PATCH] ticket status --- .../dto/update-ticket-status-user.dto.ts | 13 ++++++++++ .../ticket/dto/update-ticket-status.dto.ts | 12 +++++++++ src/modules/ticket/ticket.controller.ts | 21 ++++++++++++++++ src/modules/ticket/ticket.service.ts | 25 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 src/modules/ticket/dto/update-ticket-status-user.dto.ts create mode 100644 src/modules/ticket/dto/update-ticket-status.dto.ts diff --git a/src/modules/ticket/dto/update-ticket-status-user.dto.ts b/src/modules/ticket/dto/update-ticket-status-user.dto.ts new file mode 100644 index 0000000..5fedfa9 --- /dev/null +++ b/src/modules/ticket/dto/update-ticket-status-user.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsIn } from 'class-validator'; +import { TicketStatusEnum } from '../enum/status.enum'; + +export class UpdateTicketStatusUserDto { + @ApiProperty({ + description: 'User can only set status to closed', + enum: [TicketStatusEnum.CLOSED], + example: TicketStatusEnum.CLOSED, + }) + @IsIn([TicketStatusEnum.CLOSED]) + status: TicketStatusEnum.CLOSED; +} diff --git a/src/modules/ticket/dto/update-ticket-status.dto.ts b/src/modules/ticket/dto/update-ticket-status.dto.ts new file mode 100644 index 0000000..e3f383d --- /dev/null +++ b/src/modules/ticket/dto/update-ticket-status.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsEnum } from 'class-validator'; +import { TicketStatusEnum } from '../enum/status.enum'; + +export class UpdateTicketStatusDto { + @ApiProperty({ + description: 'Ticket status', + enum: TicketStatusEnum, + }) + @IsEnum(TicketStatusEnum) + status: TicketStatusEnum; +} diff --git a/src/modules/ticket/ticket.controller.ts b/src/modules/ticket/ticket.controller.ts index a6d2d0e..d259205 100644 --- a/src/modules/ticket/ticket.controller.ts +++ b/src/modules/ticket/ticket.controller.ts @@ -13,8 +13,10 @@ import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; import { TicketService } from './ticket.service'; import { CreateTicketDto } from './dto/create-ticket.dto'; import { UpdateTicketDto } from './dto/update-ticket.dto'; +import { UpdateTicketStatusDto } from './dto/update-ticket-status.dto'; import { CreateTicketUserDto } from './dto/create-ticket-user.dto'; import { UpdateTicketUserDto } from './dto/update-ticket-user.dto'; +import { UpdateTicketStatusUserDto } from './dto/update-ticket-status-user.dto'; import { SearchTicketQueryDto } from './dto/search-ticket-query.dto'; import { AdminAuthGuard } from '../auth/guards/adminAuth.guard'; import { AuthGuard } from '../auth/guards/auth.guard'; @@ -63,6 +65,17 @@ export class TicketController { return this.ticketService.updateAsUser(id, updateTicketUserDto, userId); } + @Patch('public/tickets/:id/status') + @UseGuards(AuthGuard) + @ApiOperation({ summary: 'Update ticket status (user can only set to closed)' }) + updateStatusAsUser( + @Param('id') id: string, + @Body() dto: UpdateTicketStatusUserDto, + @UserId() userId: string, + ) { + return this.ticketService.updateStatusAsUser(id, dto, userId); + } + // ================== Admin routes =================== @Post('admin/tickets') @@ -97,6 +110,14 @@ export class TicketController { return this.ticketService.update(id, updateTicketDto); } + @Patch('admin/tickets/:id/status') + @UseGuards(AdminAuthGuard) + @Permissions(PermissionEnum.MANAGE_TICKETS) + @ApiOperation({ summary: 'Update ticket status (admin can set any status)' }) + updateStatusAsAdmin(@Param('id') id: string, @Body() dto: UpdateTicketStatusDto) { + return this.ticketService.updateStatusAsAdmin(id, dto); + } + @Delete('admin/tickets/:id') @UseGuards(AdminAuthGuard) @Permissions(PermissionEnum.MANAGE_TICKETS) diff --git a/src/modules/ticket/ticket.service.ts b/src/modules/ticket/ticket.service.ts index bfd4487..7cdc80c 100644 --- a/src/modules/ticket/ticket.service.ts +++ b/src/modules/ticket/ticket.service.ts @@ -2,8 +2,10 @@ import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/commo import { EntityManager } from '@mikro-orm/postgresql'; import { CreateTicketDto } from './dto/create-ticket.dto'; import { UpdateTicketDto } from './dto/update-ticket.dto'; +import { UpdateTicketStatusDto } from './dto/update-ticket-status.dto'; import { CreateTicketUserDto } from './dto/create-ticket-user.dto'; import { UpdateTicketUserDto } from './dto/update-ticket-user.dto'; +import { UpdateTicketStatusUserDto } from './dto/update-ticket-status-user.dto'; import { SearchTicketQueryDto } from './dto/search-ticket-query.dto'; import { Ticket } from './entities/ticket.entity'; import { TicketRepository } from './repositories/ticket.repository'; @@ -165,6 +167,29 @@ export class TicketService { return ticket; } + async updateStatusAsAdmin(id: string, dto: UpdateTicketStatusDto) { + const ticket = await this.ticketRepository.findOne({ id }, { filters: ['notDeleted'] }); + if (!ticket) { + throw new NotFoundException('Ticket not found'); + } + ticket.status = dto.status; + await this.em.flush(); + return ticket; + } + + async updateStatusAsUser(id: string, _dto: UpdateTicketStatusUserDto, userId: string) { + const ticket = await this.ticketRepository.findOne({ id }, { filters: ['notDeleted'] }); + if (!ticket) { + throw new NotFoundException('Ticket not found'); + } + if (ticket.user?.id !== userId) { + throw new ForbiddenException('You can only update your own tickets'); + } + ticket.status = TicketStatusEnum.CLOSED; + await this.em.flush(); + return ticket; + } + async remove(id: string) { const ticket = await this.ticketRepository.findOne({ id }, { filters: ['notDeleted'] }); if (!ticket) {