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:
@@ -72,3 +72,33 @@ export interface SuppressionEntry {
|
||||
reason: string | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface Agent {
|
||||
id: string;
|
||||
userId: string;
|
||||
extensionId: string | null;
|
||||
name: string;
|
||||
maxNoAnswer: number;
|
||||
wrapUpTime: number;
|
||||
state: string;
|
||||
enabled: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export const AGENT_STATE_LABELS: Record<string, string> = {
|
||||
OFFLINE: "Offline",
|
||||
LOGGED_IN: "Logado",
|
||||
AVAILABLE: "Disponível",
|
||||
RESERVED: "Reservado",
|
||||
RINGING: "Chamando",
|
||||
IN_CALL: "Em chamada",
|
||||
WRAP_UP: "Pós-atendimento",
|
||||
PAUSED: "Em pausa",
|
||||
};
|
||||
|
||||
export interface TenantUser {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string;
|
||||
status: string;
|
||||
}
|
||||
|
||||
67
apps/frontend/src/lib/dialplan-types.ts
Normal file
67
apps/frontend/src/lib/dialplan-types.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
/** Duplicado do backend só pra UI (mesmo padrão de QUEUE_STRATEGIES em
|
||||
* callcenter-types.ts) — o frontend não importa pacotes do backend, fala
|
||||
* só HTTP. `apps/api` continua sendo a autoridade: a allowlist aqui é só
|
||||
* pra montar o `<select>`, o servidor rejeita qualquer coisa fora dela
|
||||
* (agente.md secao 180). */
|
||||
export const ALLOWED_DIALPLAN_APPLICATIONS = [
|
||||
"answer",
|
||||
"pre_answer",
|
||||
"bridge",
|
||||
"hangup",
|
||||
"park",
|
||||
"playback",
|
||||
"ring_ready",
|
||||
"respond",
|
||||
"set",
|
||||
"export",
|
||||
"transfer",
|
||||
"sleep",
|
||||
"record_session",
|
||||
] as const;
|
||||
|
||||
export const ALLOWED_CONDITION_FIELDS = [
|
||||
"destination_number",
|
||||
"caller_id_number",
|
||||
"caller_id_name",
|
||||
"context",
|
||||
"network_addr",
|
||||
"source",
|
||||
] as const;
|
||||
|
||||
export const CONDITION_FIELD_LABELS: Record<string, string> = {
|
||||
destination_number: "Número discado",
|
||||
caller_id_number: "Número do chamador",
|
||||
caller_id_name: "Nome do chamador",
|
||||
context: "Contexto",
|
||||
network_addr: "Endereço de rede",
|
||||
source: "Origem",
|
||||
};
|
||||
|
||||
export interface DialplanAction {
|
||||
application: string;
|
||||
data?: string;
|
||||
}
|
||||
|
||||
export interface DialplanExtension {
|
||||
id: string;
|
||||
context: string;
|
||||
name: string;
|
||||
conditionField: string;
|
||||
conditionExpr: string;
|
||||
actions: DialplanAction[];
|
||||
antiActions: DialplanAction[] | null;
|
||||
continueOnFalse: boolean;
|
||||
order: number;
|
||||
enabled: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface DialplanVersion {
|
||||
id: string;
|
||||
context: string;
|
||||
version: number;
|
||||
status: "DRAFT" | "ACTIVE" | "SUPERSEDED";
|
||||
createdAt: string;
|
||||
activatedAt: string | null;
|
||||
createdByUserId: string;
|
||||
}
|
||||
Reference in New Issue
Block a user