feat(platform): Clientes > Tenants e Planos — CRUD real (backend novo)
Não existia NENHUM endpoint pra criar/listar/editar tenant nem plano até aqui — só via script/seed ad hoc. Dois controllers novos: TenantsController (/tenants) e PlansController (/plans), platform-only. POST /tenants cria o tenant E o primeiro usuário (Tenant Admin) numa transação só — sem esse usuário o tenant fica inacessível. Senha gerada e devolvida em texto puro só na resposta de criação (revela uma vez, mesmo padrão de Ramais/SIP). Bug real achado testando o próprio endpoint: GET /tenants calculava memberCount sem contexto de RLS — tenant_memberships tem FORCE RLS, então nem platform admin enxerga linha nenhuma sem app.current_tenant_id setado, o campo sempre voltava 0. Corrigido abrindo o contexto de cada tenant um de cada vez. Frontend: /platform/clientes/tenants (lista+busca), /tenants/new (cria tenant+admin, revela senha), /tenants/:id (troca status/plano, preview dos limites ao vivo), /platform/clientes/planos (CRUD completo dos limites). Testado ponta a ponta: plano novo -> tenant novo com admin real -> troca de plano persistida, confirmada via API. Smoke test nas 19 telas anteriores, todas 200. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
61
apps/frontend/src/lib/platform-types.ts
Normal file
61
apps/frontend/src/lib/platform-types.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
export interface Plan {
|
||||
id: string;
|
||||
key: string;
|
||||
name: string;
|
||||
maxExtensions: number | null;
|
||||
maxAgents: number | null;
|
||||
maxTrunks: number | null;
|
||||
maxQueues: number | null;
|
||||
maxCampaigns: number | null;
|
||||
maxCps: number | null;
|
||||
maxConcurrentCalls: number | null;
|
||||
maxDailyCalls: number | null;
|
||||
maxMonthlyCalls: number | null;
|
||||
maxRecordingStorageGb: number | null;
|
||||
recordingRetentionDays: number | null;
|
||||
transcriptionRetentionDays: number | null;
|
||||
recordingEnabled: boolean;
|
||||
aiEnabled: boolean;
|
||||
aiTranscriptionEnabled: boolean;
|
||||
aiAnalysisEnabled: boolean;
|
||||
apiAccessEnabled: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Tenant {
|
||||
id: string;
|
||||
code: string;
|
||||
slug: string;
|
||||
legalName: string;
|
||||
tradeName: string | null;
|
||||
taxId: string | null;
|
||||
status: "TRIAL" | "ACTIVE" | "SUSPENDED" | "PAST_DUE" | "CANCELLED";
|
||||
timezone: string;
|
||||
locale: string;
|
||||
billingCurrency: string;
|
||||
planId: string;
|
||||
plan: { id: string; key: string; name: string };
|
||||
memberCount: number;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export const TENANT_STATUS_LABELS: Record<string, string> = {
|
||||
TRIAL: "Trial",
|
||||
ACTIVE: "Ativo",
|
||||
SUSPENDED: "Suspenso",
|
||||
PAST_DUE: "Inadimplente",
|
||||
CANCELLED: "Cancelado",
|
||||
};
|
||||
|
||||
export const PLAN_LIMIT_FIELDS: { key: keyof Plan; label: string }[] = [
|
||||
{ key: "maxExtensions", label: "Ramais" },
|
||||
{ key: "maxAgents", label: "Agentes" },
|
||||
{ key: "maxTrunks", label: "Troncos" },
|
||||
{ key: "maxQueues", label: "Filas" },
|
||||
{ key: "maxCampaigns", label: "Campanhas" },
|
||||
{ key: "maxCps", label: "CPS" },
|
||||
{ key: "maxConcurrentCalls", label: "Chamadas simultâneas" },
|
||||
{ key: "maxDailyCalls", label: "Chamadas/dia" },
|
||||
{ key: "maxMonthlyCalls", label: "Chamadas/mês" },
|
||||
{ key: "maxRecordingStorageGb", label: "Armazenamento (GB)" },
|
||||
];
|
||||
Reference in New Issue
Block a user