fix(filas): adiciona edição de fila após criação

Achado real reportado pelo usuário: "na fila nao tem opcao de editar a
fila apos a criacao". queues.controller.ts só tinha create/list/delete
— nunca existiu PATCH.

UpdateQueueDto (mesmos campos do create, todos opcionais) + PATCH
/queues/:id. Tela ganhou um botão "Editar" por linha, abrindo um
formulário inline pré-preenchido (reaproveita o mesmo componente do
"Nova fila", só muda entre createQueue/updateQueue).

Testado ponta a ponta com Playwright de verdade (não só a API): abrir o
form de edição confirmando que veio pré-preenchido com os valores reais
da fila, editar nome e espera máxima, salvar, e confirmar que a tabela
atualizou com os novos valores.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-30 20:13:03 -03:00
parent 5bbe9e6800
commit 26cc79cec2
4 changed files with 223 additions and 42 deletions

View File

@@ -86,3 +86,80 @@ export class CreateQueueDto {
@IsBoolean()
recordingEnabled?: boolean;
}
export class UpdateQueueDto {
@IsOptional()
@IsString()
@MaxLength(80)
name?: string;
@IsOptional()
@IsString()
@MaxLength(255)
description?: string;
@IsOptional()
@IsIn(STRATEGIES)
strategy?: (typeof STRATEGIES)[number];
@IsOptional()
@IsString()
@MaxLength(255)
mohSound?: string;
@IsOptional()
@IsString()
@MaxLength(255)
announceSound?: string;
@IsOptional()
@IsInt()
@Min(0)
@Max(3600)
announceFrequency?: number;
@IsOptional()
@IsInt()
@Min(0)
@Max(3600)
maxWaitTime?: number;
@IsOptional()
@IsInt()
@Min(0)
@Max(3600)
maxWaitTimeWithNoAgent?: number;
@IsOptional()
@IsString()
@MaxLength(80)
agentNoAnswerStatus?: string;
@IsOptional()
@IsBoolean()
tierRulesApply?: boolean;
@IsOptional()
@IsInt()
@Min(1)
@Max(3600)
tierRuleWaitSecond?: number;
@IsOptional()
@IsInt()
@Min(0)
@Max(3600)
discardAbandonedAfter?: number;
@IsOptional()
@IsBoolean()
abandonedResumeAllowed?: boolean;
@IsOptional()
@IsBoolean()
skipAgentsWithExternalCalls?: boolean;
@IsOptional()
@IsBoolean()
recordingEnabled?: boolean;
}

View File

@@ -7,6 +7,7 @@ import {
HttpStatus,
NotFoundException,
Param,
Patch,
Post,
UseGuards,
} from "@nestjs/common";
@@ -18,7 +19,7 @@ import { PermissionGuard } from "../common/guards/permission.guard";
import { RequirePermission } from "../common/decorators/require-permission.decorator";
import { CurrentUser } from "../common/decorators/current-user.decorator";
import { getRedisClient } from "../common/redis";
import { CreateQueueDto } from "./dto/create-queue.dto";
import { CreateQueueDto, UpdateQueueDto } from "./dto/create-queue.dto";
const QUEUES_SYNC_CHANNEL = "b2bcall:queues:sync";
@@ -101,6 +102,58 @@ export class QueuesController {
return queue;
}
/**
* Achado real reportado pelo usuário: "na fila nao tem opcao de editar
* a fila apos a criacao" — só existia create/delete até aqui.
*/
@RequirePermission("queues.manage")
@Patch(":id")
async update(@CurrentUser() user: AccessTokenClaims, @Param("id") id: string, @Body() dto: UpdateQueueDto) {
const prisma = getPrismaClient();
const tenantId = user.tenantId!;
const result = await withTenantContext(prisma, tenantId, (tx) =>
tx.queue.updateMany({
where: { id, tenantId, deletedAt: null },
data: {
...(dto.name !== undefined ? { name: dto.name } : {}),
...(dto.description !== undefined ? { description: dto.description } : {}),
...(dto.strategy !== undefined ? { strategy: dto.strategy } : {}),
...(dto.mohSound !== undefined ? { mohSound: dto.mohSound } : {}),
...(dto.announceSound !== undefined ? { announceSound: dto.announceSound } : {}),
...(dto.announceFrequency !== undefined ? { announceFrequency: dto.announceFrequency } : {}),
...(dto.maxWaitTime !== undefined ? { maxWaitTime: dto.maxWaitTime } : {}),
...(dto.maxWaitTimeWithNoAgent !== undefined ? { maxWaitTimeWithNoAgent: dto.maxWaitTimeWithNoAgent } : {}),
...(dto.agentNoAnswerStatus !== undefined ? { agentNoAnswerStatus: dto.agentNoAnswerStatus } : {}),
...(dto.tierRulesApply !== undefined ? { tierRulesApply: dto.tierRulesApply } : {}),
...(dto.tierRuleWaitSecond !== undefined ? { tierRuleWaitSecond: dto.tierRuleWaitSecond } : {}),
...(dto.discardAbandonedAfter !== undefined ? { discardAbandonedAfter: dto.discardAbandonedAfter } : {}),
...(dto.abandonedResumeAllowed !== undefined ? { abandonedResumeAllowed: dto.abandonedResumeAllowed } : {}),
...(dto.skipAgentsWithExternalCalls !== undefined ? { skipAgentsWithExternalCalls: dto.skipAgentsWithExternalCalls } : {}),
...(dto.recordingEnabled !== undefined ? { recordingEnabled: dto.recordingEnabled } : {}),
},
}),
);
if (result.count === 0) {
throw new NotFoundException();
}
const updated = await withTenantContext(prisma, tenantId, (tx) => tx.queue.findFirstOrThrow({ where: { id } }));
await recordAuditEvent(prisma, {
action: "QUEUE_UPDATE",
tenantId,
userId: user.sub,
entityType: "queue",
entityId: id,
after: { ...dto },
});
await notifyQueuesChanged();
return updated;
}
@RequirePermission("queues.manage")
@Delete(":id")
@HttpCode(HttpStatus.NO_CONTENT)