feat: Administração > Configurações, remover usuário do tenant, Discador > Callbacks
Fecha os últimos gaps do módulo Administração (agente.md secao 169): tela de Configurações self-service do próprio tenant (GET/PATCH /tenant-settings, nunca aceita tenantId arbitrário — só user.tenantId das claims), e DELETE /users/:id pra remover alguém do tenant, com duas proteções que não existiam antes (não deixa remover a si mesmo, não deixa remover/rebaixar o último Tenant Admin). Corrige um bug real achado testando a remoção: o delete de TenantMembership (FORCE RLS) rodava dentro de um prisma.$transaction([...]) em forma de array, que nunca seta app.current_tenant_id — Prisma devolvia P2025 "not found" com a linha existindo (500 pro cliente). Mesma classe de bug já corrigida antes em TenantsController.create; corrigido com $transaction(async (tx) => ...) + set_config explícito. Adiciona Discador > Callbacks (GET/PATCH /leads/callbacks, tenant-wide): reagendar, tentar de novo sem esperar, ou desistir de um lead que pediu retorno em outro horário. Remove "Importações" do menu — decisão já registrada na PHASE 39 de não duplicar uma tela pro que já existe (CSV em lote no wizard/detalhe da campanha). Testado ponta a ponta via curl e Puppeteer contra o tenant Acme real: tenant-settings GET/PATCH, proteção de último-admin nos dois endpoints que a usam, convite+remoção de um admin temporário, e o fluxo completo de callback (lead forçado pra CALLBACK via SQL, reagendar rejeitado pro passado/aceito pro futuro, requeue confirmado). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
57
TODO.md
57
TODO.md
@@ -1483,6 +1483,63 @@ systemd (fecha um risco documentado desde a PHASE 01)
|
||||
`PredictiveDialerEngine` já popula (PHASE 16) mas sem tela nenhuma
|
||||
pra visualizar/gerenciar ainda
|
||||
|
||||
## PHASE 40 — Administração > Configurações (tenant) + últimos gaps de
|
||||
Usuários (agente.md secao 169)
|
||||
- [x] `GET/PATCH /tenant-settings`: self-service do próprio tenant — só
|
||||
lê/escreve `user.tenantId` das claims, nunca aceita um tenantId
|
||||
arbitrário no path/body, então não existe forma de um Tenant Admin
|
||||
mexer em outro tenant por aqui (diferente de `TenantsController`,
|
||||
que é platform-only). Editável: nome fantasia, CNPJ/CPF, fuso,
|
||||
idioma, privacidade de IA. Somente leitura: razão social, código,
|
||||
plano, status, moeda, domínio de telefonia (controlados pela
|
||||
plataforma)
|
||||
- [x] Tela `/app/administracao/configuracoes` — formulário + bloco
|
||||
read-only, mesmo padrão visual das outras telas de Administração
|
||||
- [x] `DELETE /users/:id` — remove só a membership+role do tenant (nunca
|
||||
a conta `User`, que pode ter acesso a outros tenants). Duas
|
||||
proteções novas (nenhuma existia antes): não deixa remover a si
|
||||
mesmo, e não deixa remover/rebaixar o último Tenant Admin do tenant
|
||||
(`isLastTenantAdmin`, aplicado também em `PATCH /users/:id/role`) —
|
||||
sem isso um tenant podia ficar sem ninguém que pudesse gerenciar
|
||||
usuários
|
||||
- [x] achado real: o primeiro `remove()` usava
|
||||
`prisma.$transaction([...])` (forma array) pra apagar
|
||||
`tenantMembership` — como essa tabela tem FORCE RLS (secao 32) e a
|
||||
forma array não abre uma transação com `app.current_tenant_id`
|
||||
setado, o Prisma devolvia P2025 ("not found") mesmo com a linha
|
||||
existindo (500 pro cliente). Mesma classe de bug já corrigida antes
|
||||
em `TenantsController.create`. Corrigido trocando pra
|
||||
`$transaction(async (tx) => ...)` com `set_config` explícito antes
|
||||
do delete — testado removendo de verdade um usuário de teste
|
||||
(invite → demote self (2 admins) → remove → 204 → sumiu da lista)
|
||||
- [x] Testado ponta a ponta: GET/PATCH tenant-settings via curl e via UI
|
||||
(nome fantasia editado, sidebar atualiza na hora), proteção de
|
||||
último-admin confirmada nos dois endpoints (403 nos dois), convite +
|
||||
remoção de um admin temporário confirmados
|
||||
|
||||
## PHASE 41 — Discador > Callbacks (agente.md secao 78-79, 169)
|
||||
- [x] `GET /leads/callbacks` (tenant-wide, todas as campanhas) +
|
||||
`PATCH /leads/callbacks/:id` com 3 ações: `RESCHEDULE` (nova data,
|
||||
rejeitada se não for no futuro), `REQUEUE` (volta pra `READY`,
|
||||
dialer pega de novo sem esperar), `CANCEL` (`DO_NOT_CALL`) — só
|
||||
atua sobre leads que ainda estão em `CALLBACK` (RLS +
|
||||
`withTenantContext`, mesmo padrão já auditado)
|
||||
- [x] Tela `/app/discador/callbacks` — lista com nome da campanha,
|
||||
telefone, tentativas, "remarcado para" com date-time picker inline
|
||||
- [x] "Importações" removido do menu (nunca virou tela real, decisão já
|
||||
registrada na PHASE 39: CSV em lote já existe no wizard e no
|
||||
detalhe da campanha, uma tela separada só faria sentido com
|
||||
histórico de import persistido, que não existe) — mesmo princípio
|
||||
já usado em Monitoramento (secao 168-169): não deixar link pra
|
||||
"em breve" quando a funcionalidade de verdade já está em outro
|
||||
lugar
|
||||
- [x] Testado ponta a ponta: lead de teste forçado pra `CALLBACK` via SQL
|
||||
direto (não existe fluxo de produto pra chegar nesse estado sem o
|
||||
dialer rodando uma chamada real), reagendamento rejeitado pro
|
||||
passado (400), aceito pro futuro (200), requeue confirmado (sai da
|
||||
lista de callbacks, volta pra `READY`), lead de teste removido no
|
||||
final
|
||||
|
||||
---
|
||||
|
||||
## Riscos conhecidos
|
||||
|
||||
Reference in New Issue
Block a user