Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ const mockChat = {
createdAt: new Date(),
};

const mockTicketQuery = {
exec: jest.fn().mockResolvedValue(null),
};

const mockTicketModel = {
findById: jest.fn().mockReturnValue(mockTicketQuery),
};

describe('ChatService — envio de mensagem com arquivo', () => {
let service: ChatService;

Expand Down
8 changes: 8 additions & 0 deletions backend/src/modules/chat/application/chat.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ const mockChat: ChatDetails = {
new Date('2026-01-01'),
};

const mockTicketQuery = {
exec: jest.fn().mockResolvedValue(null),
};

const mockTicketModel = {
findById: jest.fn().mockReturnValue(mockTicketQuery),
};

describe('ChatService', () => {

let service: ChatService;
Expand Down
5 changes: 5 additions & 0 deletions backend/src/modules/chat/presentation/chat.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@ export class ChatController {
if (!chat) throw new NotFoundException('Chat não encontrado');
return chat;
}

@Get(':id')
async getChatById(@Param('id') id: string) {
return this.chatService.getChatById(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Request,
UseGuards,
Query,
BadRequestException,
} from '@nestjs/common';
import { CreateTicketUseCase } from '../../application/useCases/create/create.usecase';
import { DeleteTicketUseCase } from '../../application/useCases/delete/delete.usecase';
Expand All @@ -28,6 +29,7 @@ import { CreateTicketRequest } from '../dtos/create.dto';
import { EscalateTicketRequest } from '../dtos/escalateTicket.dto';
import { TicketMapper } from '../mappers/ticket.mapper';
import { CloseTicketRequest } from '../dtos/closeTicket.dto';
import { UpdateTicketStatusRequest } from '../dtos/updateTicketStatus.dto';
import {
ApiBearerAuth,
ApiBody,
Expand Down Expand Up @@ -210,4 +212,46 @@ export class TicketController {

return response;
}

@Put(':id/status')
@ApiOperation({ summary: 'Altera o status de um ticket de forma genérica e integrada' })
@ApiParam({ name: 'id', example: 'uuid-do-ticket' })
@ApiBody({ type: UpdateTicketStatusRequest })
@UseGuards(JwtGuard, RolesGuard)
@Roles(UserRole.ADMIN, UserRole.SUPPORT)
@ApiResponse({ status: 200, description: 'Status do ticket alterado com sucesso.' })
async updateStatus(
@Request() req: any,
@Param('id') id: string,
@Body() body: UpdateTicketStatusRequest,
) {
if (body.status === TicketStatus.IN_PROGRESS) {
const data = TicketMapper.toNewAgentInput(id, req.user.id);
return await this.newAgentUseCase.execute(data);
}

if (body.status === TicketStatus.ESCALATED) {
if (!body.groupId) {
throw new BadRequestException('Escalonamento exige groupId.');
}
const data = TicketMapper.toEscalateTicketInput(id, {
groupId: body.groupId,
category: body.category || '',
whatWasDone: body.whatWasDone || '',
});
return await this.escalateUseCase.execute(data);
}

if (body.status === TicketStatus.CLOSED) {
if (!body.solution) {
throw new BadRequestException('Fechamento do chamado exige uma solução descrita.');
}
const data = TicketMapper.toCloseTicketInput(id, {
solution: body.solution,
});
return await this.closeUseCase.execute(data);
}

throw new BadRequestException('Transição de status inválida ou não suportada.');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString, IsNotEmpty, IsEnum, IsOptional } from 'class-validator';
import { TicketStatus } from '../../domain/entities/ticket.entity';

export class UpdateTicketStatusRequest {
@ApiProperty({
example: 'IN_PROGRESS',
enum: TicketStatus,
})
@IsEnum(TicketStatus)
@IsNotEmpty()
status: TicketStatus;

@ApiProperty({
example: 'O problema foi resolvido reiniciando o servidor.',
required: false,
})
@IsString()
@IsOptional()
solution?: string;

@ApiProperty({
example: 'uuid-do-grupo',
required: false,
})
@IsString()
@IsOptional()
groupId?: string;

@ApiProperty({
example: 'web_app',
required: false,
})
@IsString()
@IsOptional()
category?: string;

@ApiProperty({
example: 'Foi feita a verificação física...',
required: false,
})
@IsString()
@IsOptional()
whatWasDone?: string;
}
Loading