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:
@@ -16,3 +16,25 @@ export const TENANT_ROLE_OPTIONS: { key: string; name: string }[] = [
|
||||
{ key: "supervisor", name: "Supervisor" },
|
||||
{ key: "agent", name: "Agente" },
|
||||
];
|
||||
|
||||
export interface TenantSettings {
|
||||
id: string;
|
||||
code: string;
|
||||
legalName: string;
|
||||
tradeName: string | null;
|
||||
taxId: string | null;
|
||||
status: "TRIAL" | "ACTIVE" | "SUSPENDED" | "PAST_DUE" | "CANCELLED";
|
||||
timezone: string;
|
||||
locale: string;
|
||||
billingCurrency: string;
|
||||
telephonyDomain: string | null;
|
||||
aiPrivacyLevel: "AI_OFF" | "TRANSCRIPTION_ONLY" | "TRANSCRIPTION_AND_ANALYSIS";
|
||||
plan: { key: string; name: string };
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export const AI_PRIVACY_LEVEL_LABELS: Record<string, string> = {
|
||||
AI_OFF: "Desligada — nenhum áudio/transcrição sai deste tenant",
|
||||
TRANSCRIPTION_ONLY: "Só transcrição — sem análise por IA",
|
||||
TRANSCRIPTION_AND_ANALYSIS: "Transcrição e análise por IA",
|
||||
};
|
||||
|
||||
@@ -88,6 +88,10 @@ export interface Lead {
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface CallbackLead extends Lead {
|
||||
campaign: { id: string; name: string };
|
||||
}
|
||||
|
||||
export const LEAD_STATUS_LABELS: Record<string, string> = {
|
||||
NEW: "Novo",
|
||||
READY: "Pronto",
|
||||
|
||||
Reference in New Issue
Block a user