feat: Call Center > Agentes + Telefonia > Dialplan (novo endpoint GET /users)

Fecha a lacuna de backend que travava Agentes: POST /agents exigia um
userId de um usuário já existente do tenant, mas não havia nenhum endpoint
pra listar usuários. Novo GET /users (gated por users.manage, não
agents.manage — listar identidade de login é administração de usuário)
via TenantMembership, mesmo padrão de RLS já usado em listUserTenants.

Duas telas novas: Call Center > Agentes (criar/listar/remover, seletor de
usuário só mostra quem ainda não é agente) e Telefonia > Dialplan (editor
estruturado do contexto default — regras com condição/ações dinâmicas,
painel de Versões com gerar/ativar, reativar versão antiga = rollback).

Testado ponta a ponta contra a API real do tenant Acme, incluindo o ciclo
completo de dialplan (criar regra -> gerar v1 -> ativar -> badge "Ativa").
Smoke test de regressão nas 13 telas anteriores do tenant + platform.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-29 16:10:45 -03:00
parent ca49504f0a
commit f1cadddc91
23 changed files with 1025 additions and 12 deletions

58
TODO.md
View File

@@ -948,16 +948,6 @@ Troncos, Discador > Lista de Bloqueio (agente.md secao 50-51, 41-42, 71,
- [ ] **Escopo real ainda faltando pra "aplicação finalizada" segundo o
agente.md completo** (236 seções) — não coberto nesta fase, listado
explicitamente em vez de fingir completo:
- Call Center > Agentes (frontend): bloqueado por uma lacuna de
backend, não só falta de tela — `POST /agents` exige um `userId`
de um usuário já existente do tenant, mas **não existe nenhum
endpoint pra listar/convidar usuários do tenant** ainda (só
criação de tenant/usuário via script, mesma lacuna já anotada na
PHASE 22 pra tenant/plano/assinatura). Precisa de um "Usuários"
(Administração) antes de Agentes fazer sentido na UI.
- Telefonia > Dialplan (editor estruturado, condition/actions
dinâmicos, generate/activate de versão) — backend pronto desde a
PHASE 10, zero UI.
- Monitoramento em tempo real (secao 54-55, 161) — WebSocket já
existe no backend (`RealtimeGateway`), zero cliente no frontend.
- Gravações (player de áudio, secao 121) e IA > Scorecards/Prompts/
@@ -977,6 +967,54 @@ Troncos, Discador > Lista de Bloqueio (agente.md secao 50-51, 41-42, 71,
continuam sem existir — o acesso de teste é direto na porta 3001
da VM, sem HTTPS.
## PHASE 29 — Frontend: Call Center > Agentes, Telefonia > Dialplan
(agente.md secao 45, 43-44)
- [x] **Lacuna de backend fechada primeiro**: `GET /users` (novo,
`apps/api/src/users/`) — lista os usuários do tenant ativo via
`TenantMembership` (join, mesma tabela/RLS já usada em
`listUserTenants`), gated por `users.manage` (não `agents.manage`
de propósito: listar identidade de login é administração de
usuário, não de call center — um supervisor com `agents.manage` mas
sem `users.manage` gerencia agentes já provisionados, mas não lista
usuários pra criar um novo). Só o necessário pra desbloquear o
seletor de Agentes; convite/criação de usuário continua só via
script.
- [x] Call Center > Agentes (`/app/callcenter/agentes`): criar (usuário +
nome + ramal opcional + máx. não-atendidas + pós-atendimento),
listar (usuário/e-mail, ramal, estado), remover (soft delete). O
seletor de usuário só mostra quem ainda não é agente (filtro
client-side, backend segue sendo a autoridade); ramal segue a mesma
lógica. Estado do agente (`AgentState`) tem badge própria — **não**
reusa `StatusBadge`: `PAUSED` já existe nesse mapa global pro
status de campanha ("Pausada"), reaproveitar mostraria o texto
errado pra um agente em pausa.
- [x] Telefonia > Dialplan (`/app/telefonia/dialplan`): editor estruturado
do contexto `default` (fixo nesta primeira versão — todo o sistema
só usou um contexto até aqui) — criar regra (nome, campo/expressão
da condição, lista dinâmica de ações com a mesma allowlist do
backend, ordem, continuar-se-falhar), listar em ordem de avaliação,
remover. Painel de Versões: gerar rascunho a partir das regras
atuais, ativar (reativar uma versão antiga é o rollback, sem
endpoint separado — mesmo texto já usado no backend). `ACTIVE`/
`SUPERSEDED` novos no mapa de `StatusBadge`; `DRAFT` reusa o rótulo
que já existia pra campanha (mesmo conceito, sem conflito).
- [x] Testado ponta a ponta contra a API real (tenant Acme): Agentes —
criar agente ligado ao próprio usuário logado (único usuário do
tenant neste estágio) → aparece na lista com estado `Offline` →
remover → volta vazio, botão "Novo agente" reabilitado. Dialplan —
criar regra `destination_number ~ ^1[0-9]{3}$` com ação
`bridge(${destination_number})` → gerar v1 (DRAFT) → ativar → badge
"Ativa" confirmado. Smoke test de regressão nas 13 telas anteriores
do tenant + platform, todas 200 sem quebrar nada.
- [ ] Sem paginação/uniqueness real em `Agent.userId`/`extensionId` — o
"só quem não é agente ainda" é conveniência de UI, não constraint de
banco (mesma classe de lacuna sistêmica da PHASE 12, `@@unique` +
soft delete)
- [ ] Dialplan: só 1 contexto (`default`), sem editor de `antiActions`,
sem reordenar por drag-and-drop (edita `order` como número) — todas
decisões deliberadas de escopo, mesmas simplificações já existentes
no backend desde a PHASE 10
---
## Riscos conhecidos