feat(frontend): Discador > Leads (tela dedicada)

/app/discador/leads — seletor de campanha, busca por telefone/nome,
adicionar lead individual, remover com confirmação de 2 cliques. O
backend (GET/POST/DELETE /campaigns/:campaignId/leads) já existia desde a
PHASE 15; a prévia de leads no detalhe da campanha já apontava pra esta
tela ("a lista completa vive em Discador > Leads") desde a PHASE 26, só
faltava construir.

LEAD_STATUS_LABELS novo (16 valores) — badge própria, não reusa
StatusBadge (mesma cautela de gênero de AgentStateBadge/TenantStatusBadge:
READY/FAILED/COMPLETED colidiriam com rótulos de campanha).

Testado ponta a ponta: campanha de teste criada, 3 leads adicionados (2
via API, 1 via UI), busca filtrando corretamente, remoção confirmada via
API. Smoke test nas 19 telas do tenant + platform, todas 200.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
This commit is contained in:
2026-08-30 00:44:09 -03:00
parent 4480e9265e
commit 52e1b4f3b7
10 changed files with 362 additions and 1 deletions

View File

@@ -73,3 +73,36 @@ export const DAYS_OF_WEEK_LABELS: Record<number, string> = {
6: "Sáb",
7: "Dom",
};
export interface Lead {
id: string;
campaignId: string;
name: string | null;
phoneOriginal: string;
phoneNormalized: string;
status: string;
attemptCount: number;
lastAttemptAt: string | null;
nextAttemptAt: string | null;
lastResult: string | null;
createdAt: string;
}
export const LEAD_STATUS_LABELS: Record<string, string> = {
NEW: "Novo",
READY: "Pronto",
RESERVED: "Reservado",
ORIGINATING: "Discando",
RINGING: "Chamando",
ANSWERED: "Atendeu",
QUEUEING: "Na fila",
CONNECTED_AGENT: "Com agente",
BUSY: "Ocupado",
NO_ANSWER: "Não atendeu",
FAILED: "Falhou",
VOICEMAIL: "Caixa postal",
CALLBACK: "Callback",
COMPLETED: "Concluído",
DO_NOT_CALL: "Não ligar",
MAX_ATTEMPTS: "Máx. tentativas",
};