feat(frontend): Discador > Campanhas — wizard de 7 passos, ciclo de vida, leads

Tela completa de campanhas do discador preditivo: listagem com busca/
filtro/status, wizard de criacao em 7 passos exatamente como a
especificacao nomeia (Geral -> Telefonia -> Discagem -> Horarios ->
Gravacao e IA -> Leads -> Revisao, agente.md secao 170) — nada e criado
ate confirmar na revisao, upload de CSV de leads na mesma acao de
criacao — e detalhe com acoes de ciclo de vida (iniciar/pausar/drenar/
parar, cada botao so aparece quando a transicao e valida pro status
atual), pacing ao vivo, previa de leads com import adicional, e remover.

Corrige de quebra um erro real de build: Server Actions exportadas como
arrow function que so repassam argumentos pra outra funcao quebram
("Server Actions must be async functions") — precisam ser declaradas
como async function de verdade.

Testado ponta a ponta com fila+tronco reais: os 7 passos preenchidos e
revisados, campanha criada com CSV de 3 leads importado, ciclo de vida
completo start->pause->drain->stop->delete via API, e confirmado que uma
campanha iniciada de verdade e pega pelo PredictiveDialerEngine real
rodando em Docker (stats deixam de ser null depois de alguns segundos).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EWHKmcVJtstQFErbZ1AanY
This commit is contained in:
2026-08-29 10:55:08 -03:00
parent 34ef408f00
commit 927a2623c2
22 changed files with 1375 additions and 10 deletions

View File

@@ -0,0 +1,75 @@
export type CampaignStatus = "DRAFT" | "READY" | "WAITING_SCHEDULE" | "RUNNING" | "PAUSED" | "DRAINING" | "STOPPED" | "COMPLETED" | "ERROR";
export interface Campaign {
id: string;
tenantId: string;
name: string;
description: string | null;
queueId: string;
trunkId: string;
callerIdName: string | null;
callerIdNumber: string | null;
timezone: string;
startDate: string | null;
endDate: string | null;
daysOfWeek: number[];
startTime: string | null;
endTime: string | null;
maxCps: number | null;
maxConcurrentCalls: number | null;
pacingInitial: number;
pacingMin: number;
pacingMax: number;
targetAbandonRate: number;
ringTimeout: number;
maxAttempts: number;
recordingEnabled: boolean;
avmdEnabled: boolean;
aiTranscriptionEnabled: boolean;
aiAnalysisEnabled: boolean;
status: CampaignStatus;
createdAt: string;
updatedAt: string;
}
export interface CampaignStats {
status: CampaignStatus;
stats: {
answerProbability: number | null;
averageAnswerDelay: number | null;
averageTalkTime: number | null;
abandonRate: number | null;
pacingFactor: number;
};
agentsByState: Record<string, number>;
callsInFlight: number;
}
export interface LeadImportSummary {
total: number;
valid: number;
invalid: number;
duplicates: number;
imported: number;
suppressed: number;
}
export interface QueueOption {
id: string;
name: string;
}
export interface TrunkOption {
id: string;
name: string;
}
export const DAYS_OF_WEEK_LABELS: Record<number, string> = {
1: "Seg",
2: "Ter",
3: "Qua",
4: "Qui",
5: "Sex",
6: "Sáb",
7: "Dom",
};