Files
COMERCIAL-GTI/public/index.html
T
Gustavo 4f736562f3 Carry over pre-existing WABA outreach automation WIP
This work already existed uncommitted in the main working tree (not
authored by this branch) and is required as a runtime dependency for the
campaign outreach port (zpro-client, templates, outreach routes/UI). Copied
in as-is, untouched, so this feature branch has a working baseline; the
original uncommitted state in the main checkout is left exactly as it was.
2026-08-01 10:12:25 -03:00

486 lines
22 KiB
HTML

<!doctype html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>GTI Máquina Comercial</title>
<link rel="stylesheet" href="/css/app.css">
<link rel="icon" href="/img/logo-gti.png">
</head>
<body>
<header class="topbar">
<div class="brand">
<img class="brand-mark" src="/img/logo-gti.png" alt="GTI">
<span class="brand-name">Máquina Comercial</span>
</div>
<nav class="tabs" role="tablist">
<button class="tab active" data-tab="captura" role="tab" aria-selected="true">📡 Capturar Leads</button>
<button class="tab" data-tab="tratamento" role="tab" aria-selected="false">📊 Tratar Leads</button>
</nav>
<span class="muted" id="usuario-logado"></span>
<button class="btn-ghost hidden" id="btn-usuarios" title="Usuários">👤 Usuários</button>
<button class="btn-ghost" id="btn-settings" title="Configurações">⚙️</button>
<button class="btn-ghost" id="btn-sair" title="Sair">🚪 Sair</button>
</header>
<main>
<!-- ==================== CAPTURA ==================== -->
<section id="tab-captura" class="tab-panel active">
<div class="captura-wrap">
<div class="card" id="captura-form">
<div class="row space-between">
<h2>Nova captura</h2>
<button class="btn-ghost" id="btn-ver-historico" type="button">🕓 Histórico de capturas</button>
</div>
<div class="field">
<label for="sel-uf">1. Estado (UF)</label>
<div class="row">
<select id="sel-uf"></select>
<span id="uf-status" class="chip"></span>
</div>
<div id="extracao-aviso" class="notice hidden">
<p>A base CNPJ de <strong id="extracao-uf"></strong> ainda não foi extraída neste computador.
A extração lê os arquivos da Receita Federal e demora <strong>~40 a 60 minutos</strong> (uma única vez por estado).</p>
<button class="btn" id="btn-extrair">⛏️ Extrair base agora</button>
</div>
</div>
<div class="field">
<label>2. Cidades</label>
<div class="row">
<label class="radio"><input type="radio" name="cidades-modo" value="todas" checked> Todas as cidades da UF</label>
<label class="radio"><input type="radio" name="cidades-modo" value="escolher"> Escolher cidades</label>
</div>
<div id="cidades-picker" class="picker hidden">
<input type="search" id="busca-cidade" placeholder="Digite para buscar cidade...">
<div id="cidades-sugestoes" class="sugestoes"></div>
<div id="cidades-chips" class="chips"></div>
</div>
</div>
<div class="field">
<label>3. Nichos <span class="muted" id="nichos-contagem"></span></label>
<div class="row">
<label class="radio"><input type="radio" name="nichos-modo" value="todos" checked> Todos os nichos</label>
<label class="radio"><input type="radio" name="nichos-modo" value="escolher"> Escolher nichos</label>
</div>
<div id="nichos-picker" class="picker hidden">
<div class="row">
<input type="search" id="busca-nicho" placeholder="Filtrar nichos...">
<button class="btn-ghost" id="btn-nichos-todos" type="button">Marcar visíveis</button>
<button class="btn-ghost" id="btn-nichos-nenhum" type="button">Desmarcar todos</button>
</div>
<div id="nichos-grid" class="nichos-grid"></div>
</div>
<details class="cnae-avulso">
<summary>Não achou o nicho? Buscar qualquer atividade da Receita Federal (CNAE)</summary>
<p class="muted cnae-avulso-hint">Os 57 nichos acima já vêm com mensagem e interesse configurados. Um CNAE avulso é adicionado só para a busca — ele participa da captura normalmente.</p>
<input type="search" id="busca-cnae" placeholder="Ex.: salão de beleza, transportadora, escola...">
<div id="cnae-sugestoes" class="sugestoes"></div>
<div id="cnae-chips" class="chips"></div>
</details>
</div>
<div class="field">
<label>4. Fontes de busca</label>
<div id="fontes-lista" class="fontes"></div>
</div>
<details class="field">
<summary>Opções avançadas</summary>
<div class="row">
<label>Máx. por consulta (fontes de API)
<input type="number" id="inp-max" value="10" min="1" max="60">
</label>
<label>Limite total de consultas (0 = sem limite)
<input type="number" id="inp-query-limit" value="0" min="0">
</label>
</div>
</details>
<button class="btn btn-primary btn-lg" id="btn-capturar">🚀 Iniciar captura</button>
</div>
<div class="card hidden" id="progresso-card">
<div class="row space-between">
<h2 id="progresso-titulo">Captura em andamento</h2>
<button class="btn-danger" id="btn-cancelar">Cancelar</button>
</div>
<div class="progress-bar"><div class="progress-fill" id="progresso-fill"></div></div>
<p class="progress-msg" id="progresso-msg">iniciando...</p>
<div class="stat-row" id="progresso-stats"></div>
<div class="log" id="progresso-log"></div>
</div>
</div>
</section>
<!-- ==================== TRATAMENTO ==================== -->
<section id="tab-tratamento" class="tab-panel">
<div class="card filtros-card">
<div class="filtros" id="filtros">
<select id="f-uf" data-filtro="uf"><option value="">UF: todas</option></select>
<input list="dl-cidades" id="f-cidade" data-filtro="cidade" placeholder="Cidade: todas">
<datalist id="dl-cidades"></datalist>
<select id="f-segmento" data-filtro="segmento"><option value="">Nicho: todos</option></select>
<select id="f-classificacao" data-filtro="classificacao">
<option value="">Classificação: todas</option>
<option value="excelente">🟢 Excelente</option>
<option value="bom">🟢 Bom</option>
<option value="medio">🟡 Médio</option>
<option value="baixo">🟠 Baixo</option>
<option value="muito_baixo">🔴 Muito Baixo</option>
</select>
<select id="f-prioridade" data-filtro="prioridade">
<option value="">Prioridade: todas</option>
<option value="1">P1 — atacar agora</option>
<option value="2">P2 — esta semana</option>
<option value="3">P3 — quando sobrar</option>
<option value="4">P4 — campanhas específicas</option>
</select>
<select id="f-produto" data-filtro="produto"><option value="">Produto: todos</option></select>
<select id="f-interesse" data-filtro="interesse"><option value="">Interesse: todos</option></select>
<select id="f-telefone" data-filtro="telefone_valido">
<option value="">WhatsApp: todos</option>
<option value="sim">Com WhatsApp</option>
<option value="nao">Sem WhatsApp</option>
</select>
<select id="f-website" data-filtro="website">
<option value="">Website: todos</option>
<option value="sim">Com website</option>
<option value="nao">Sem website</option>
</select>
<select id="f-email" data-filtro="email">
<option value="">E-mail: todos</option>
<option value="sim">Com e-mail</option>
<option value="nao">Sem e-mail</option>
</select>
<select id="f-exportado" data-filtro="exportado">
<option value="">Exportação: todos</option>
<option value="nao">Ainda não exportados</option>
<option value="sim">Já exportados</option>
</select>
<input type="search" id="f-busca" data-filtro="q" placeholder="🔎 empresa, CNPJ ou WhatsApp">
<button class="btn-ghost" id="btn-limpar-filtros">Limpar</button>
</div>
</div>
<div class="stat-tiles" id="stat-tiles"></div>
<div class="grid-3">
<div class="card">
<div class="row space-between">
<h2>Top cidades</h2>
</div>
<div id="rank-cidades" class="ranking"></div>
</div>
<div class="card">
<div class="row space-between">
<h2>Top nichos</h2>
</div>
<div id="rank-segmentos" class="ranking"></div>
</div>
<div class="card">
<div class="row space-between">
<h2 id="rank-produtos-titulo">Top produto recomendado</h2>
</div>
<div id="rank-produtos" class="ranking"></div>
</div>
</div>
<div class="grid-3">
<div class="card" id="card-oportunidades-dia"></div>
<div class="card" id="card-maior-oportunidade"></div>
<div class="card" id="card-evolucao-diaria"></div>
</div>
<div class="card" id="card-funil"></div>
<div class="card">
<div class="row space-between wrap">
<h2>Leads <span class="muted" id="leads-total"></span></h2>
<button class="btn btn-primary" id="btn-exportar">📤 Gerar relatório / exportação com este filtro</button>
</div>
<div class="table-wrap">
<table id="tabela-leads">
<thead>
<tr>
<th class="acao-col">Ação</th><th>Empresa</th><th>WhatsApp</th><th>Cidade</th><th>Nicho</th>
<th>Prioridade</th><th class="num">Geral</th>
<th>Classificação</th><th>Produto</th><th>Exportado</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="row space-between">
<button class="btn-ghost" id="pg-prev">← Anterior</button>
<span class="muted" id="pg-info"></span>
<button class="btn-ghost" id="pg-next">Próxima →</button>
</div>
</div>
<div class="card">
<h2>Exportações geradas</h2>
<div id="historico-exports" class="lista"></div>
</div>
</section>
</main>
<!-- Modal de exportação -->
<dialog id="modal-export">
<form method="dialog" class="modal-body" id="form-export">
<h2>Gerar relatório / exportação</h2>
<p class="muted" id="export-recap"></p>
<div id="export-atalhos" class="chips"></div>
<div class="field">
<label>Tipo de arquivo</label>
<label class="radio"><input type="radio" name="export-tipo" value="excel" checked>
📊 <strong>Planilha (CSV + Excel)</strong> — lista completa com score, à prova de notação científica</label>
<label class="radio"><input type="radio" name="export-tipo" value="zpro-oportunidades">
🗂️ <strong>Z-PRO: Oportunidades (JSONL)</strong> — payloads prontos para createOpportunity</label>
<label class="radio"><input type="radio" name="export-tipo" value="zpro-bulk">
📨 <strong>Z-PRO: Disparo em massa (CSV)</strong> — dataInput para BulkSendMessageWithVariable</label>
</div>
<div class="field" id="export-zpro-params">
<label>Parâmetros Z-PRO</label>
<div class="row">
<label>Pipeline <input type="number" id="zp-pipeline" min="1"></label>
<label>Etapa <input type="number" id="zp-stage" min="1"></label>
<label>Responsável <input type="number" id="zp-responsible" min="1"></label>
</div>
</div>
<div class="field hidden" id="export-mensagem-wrap">
<label>Mensagem sugerida (use {empresa}, {cidade}, {segmento})</label>
<textarea id="export-mensagem" rows="3">Olá {empresa}! Somos do Grupo GTI e ajudamos empresas de {cidade} com sistema de gestão, TI e atendimento automático no WhatsApp. Podemos conversar?</textarea>
</div>
<label class="radio"><input type="checkbox" id="export-marcar" checked>
Marcar estes leads como exportados (não aparecem de novo no filtro "ainda não exportados")</label>
<div class="row space-between">
<button class="btn-ghost" value="cancel">Cancelar</button>
<button class="btn btn-primary" id="btn-confirmar-export" value="default">Gerar arquivos</button>
</div>
</form>
</dialog>
<!-- Modal de histórico de capturas -->
<dialog id="modal-historico">
<div class="modal-body">
<div class="row space-between">
<h2>Últimas capturas</h2>
<button class="btn-ghost" id="btn-fechar-historico" type="button">Fechar</button>
</div>
<div id="historico-capturas" class="lista"></div>
</div>
</dialog>
<!-- Modal de configurações -->
<dialog id="modal-settings">
<form method="dialog" class="modal-body">
<div class="settings-header">
<div>
<h2>Configurações</h2>
<p class="muted">Ajustes de envio, credenciais Z-PRO e preferências comerciais.</p>
</div>
<span class="chip" id="outreach-status"></span>
</div>
<section class="settings-card settings-principal">
<div>
<label class="settings-title">Envio WhatsApp</label>
<p class="muted">O limite vale para envios manuais e automáticos feitos hoje.</p>
</div>
<div class="settings-controls">
<label class="toggle"><input type="checkbox" id="outreach-ativo"> Automação ativa</label>
<label>Limite por dia <input type="number" id="outreach-limite" min="1"></label>
</div>
</section>
<details class="settings-section" open>
<summary>Enviar mensagem manual</summary>
<p class="muted">Cria ou reutiliza um ticket no painel Z-PRO e respeita o limite diário.</p>
<div class="manual-send-card">
<label class="full">Número do WhatsApp
<input type="text" id="manual-numero" placeholder="5511999999999" inputmode="numeric">
</label>
<div class="manual-channel full" aria-label="Canal de envio manual">
<label class="radio"><input type="radio" name="manual-canal" id="manual-canal-naooficial" value="nao_oficial" checked> Não-oficial (texto livre)</label>
<label class="radio"><input type="radio" name="manual-canal" id="manual-canal-oficial" value="oficial"> Oficial (Meta, template aprovado)</label>
</div>
<label class="full" id="manual-mensagem-wrap">Mensagem
<textarea id="manual-mensagem" rows="2" placeholder="Digite a mensagem de texto livre..."></textarea>
</label>
<div id="manual-oficial-campos" class="hidden full">
<label class="full">Escolher modelo salvo
<select id="manual-template-biblioteca">
<option value="">Selecione um template...</option>
</select>
</label>
<div class="row full">
<button class="btn-ghost btn-sm" id="btn-testar-template-biblioteca" type="button">Testar modelo selecionado</button>
<span class="muted">Usa o número informado acima e variáveis de exemplo.</span>
</div>
<label>Template aprovado
<input type="text" id="manual-template-nome" placeholder="ex.: confirma_suporte">
</label>
<label>Idioma
<input type="text" id="manual-template-idioma" value="pt_BR">
</label>
<label>Rota Z-PRO
<select id="manual-template-rota">
<option value="template">Template padrão</option>
<option value="templateBody">Utility/Body</option>
<option value="templateMarketingBody">Marketing/Body</option>
</select>
</label>
<div class="template-vars full">
<label>Variáveis do template</label>
<p class="muted">Preencha na mesma ordem do template: {{1}}, {{2}}, {{3}}...</p>
<div class="variable-helper">
<select id="manual-variable-picker">
<option value="">Inserir variável da Máquina Comercial...</option>
</select>
<span class="muted">Clique em um campo abaixo e escolha uma variável.</span>
</div>
<div class="template-vars-grid">
<label class="template-var-field">Variável {{1}} <input type="text" class="manual-template-var" id="manual-template-var1" data-var-index="1" placeholder="Ex.: Gustavo"></label>
<label class="template-var-field">Variável {{2}} <input type="text" class="manual-template-var" data-var-index="2" placeholder="Ex.: Suporte GLPI #123"></label>
<label class="template-var-field">Variável {{3}} <input type="text" class="manual-template-var" data-var-index="3"></label>
<label class="template-var-field">Variável {{4}} <input type="text" class="manual-template-var" data-var-index="4"></label>
<label class="template-var-field">Variável {{5}} <input type="text" class="manual-template-var" data-var-index="5"></label>
</div>
<div class="template-preview" id="manual-template-preview">Selecione um template para ver a pré-visualização.</div>
</div>
</div>
<div class="manual-actions full">
<span class="muted" id="manual-status"></span>
<button class="btn btn-primary" id="btn-enviar-manual" type="button">Enviar mensagem</button>
</div>
<button class="btn-ghost btn-sm" id="btn-toggle-outreach-log" type="button">Ver log de envios</button>
<div id="outreach-ultimos" class="lista hidden full"></div>
</div>
</details>
<details class="settings-section">
<summary>Templates automáticos</summary>
<p class="muted">Mensagens usadas quando a automação estiver ativada. Uma por produto recomendado.</p>
<div class="template-sync-inline">
<span class="muted" id="waba-template-library-status">Carregando biblioteca de templates...</span>
<button class="btn-ghost btn-sm" id="btn-sync-gchat-templates-inline" type="button">Atualizar lista do GChat</button>
</div>
<div id="outreach-templates" class="lista"></div>
<div class="row space-between">
<span></span>
<button class="btn-ghost" id="btn-salvar-outreach" type="button">Salvar templates</button>
</div>
</details>
<details class="settings-section">
<summary>Credenciais Z-PRO</summary>
<p class="muted">Use os dados de Admin &gt; API do Z-PRO. Pode colar a Base URL com ou sem <code>https://</code>.</p>
<div class="row">
<label>Base URL <input type="text" id="cfg-zpro-baseurl" placeholder="seu-dominio-zpro.com.br"></label>
<label>API ID <input type="text" id="cfg-zpro-apiid"></label>
</div>
<div class="row">
<label>Token <input type="password" id="cfg-zpro-token" autocomplete="off"></label>
<label>WhatsApp ID <input type="text" id="cfg-zpro-whatsappid"></label>
</div>
<div class="row space-between">
<span class="muted" id="zpro-credenciais-status"></span>
<button class="btn-ghost" id="btn-testar-zpro" type="button">Testar conexão</button>
</div>
<div class="template-sync-box">
<label>URL de templates do GChat <input type="text" id="cfg-gchat-templates-url" placeholder="https://apigchat.gtigrupo.com.br/...templates..."></label>
<label>Token GChat opcional <input type="password" id="cfg-gchat-token" autocomplete="off"></label>
<div class="row space-between">
<span class="muted" id="gchat-template-sync-status"></span>
<button class="btn-ghost" id="btn-sync-gchat-templates" type="button">Sincronizar templates</button>
</div>
</div>
</details>
<details class="settings-section">
<summary>Padrões de exportação e ticket</summary>
<div class="row">
<label>Pipeline <input type="number" id="cfg-pipeline" min="1"></label>
<label>Etapa <input type="number" id="cfg-stage" min="1"></label>
<label>Responsável <input type="number" id="cfg-responsible" min="1"></label>
</div>
<div class="row">
<label>Previsão de fechamento (dias) <input type="number" id="cfg-forecast" min="1"></label>
<label>Fila ticket manual <input type="number" id="cfg-ticket-queue" min="1"></label>
</div>
</details>
<details class="settings-section zona-risco">
<summary>Zona de risco</summary>
<p class="muted">Apaga permanentemente todos os leads, exportações, capturas e extrações. Configurações e nichos não são afetados.</p>
<button class="btn-danger" id="btn-limpar-base" type="button">🗑️ Limpar toda a base de leads</button>
</details>
<div class="row space-between">
<button class="btn-ghost" value="cancel">Fechar</button>
<button class="btn btn-primary" id="btn-salvar-settings" value="default">Salvar</button>
</div>
</form>
</dialog>
<!-- Modal de confirmacao da limpeza total da base -->
<dialog id="modal-reset-base">
<div class="modal-body">
<h2>Limpar toda a base de leads</h2>
<p class="muted">Esta ação é <strong>irreversível</strong>: todos os leads, exportações, capturas e extrações serão apagados. Digite a senha de segurança para confirmar.</p>
<div class="field">
<label>Senha de segurança <input type="password" id="reset-base-senha" autocomplete="off"></label>
</div>
<p class="muted erro hidden" id="reset-base-erro"></p>
<div class="row space-between">
<button class="btn-ghost" id="btn-cancelar-reset-base" type="button">Cancelar</button>
<button class="btn-danger" id="btn-confirmar-reset-base" type="button">Confirmar limpeza</button>
</div>
</div>
</dialog>
<!-- Modal de usuarios (somente admin) -->
<dialog id="modal-usuarios">
<div class="modal-body">
<div class="row space-between">
<h2>Usuários</h2>
<button class="btn-ghost" id="btn-fechar-usuarios" type="button">Fechar</button>
</div>
<div id="lista-usuarios" class="lista"></div>
<form class="field" id="form-novo-usuario">
<label>Novo usuário</label>
<div class="row">
<input type="text" id="novo-usuario-username" placeholder="usuário" minlength="3" required>
<input type="password" id="novo-usuario-password" placeholder="senha" minlength="4" required>
<select id="novo-usuario-role">
<option value="comercial">Comercial</option>
<option value="admin">Administrador</option>
</select>
<button class="btn btn-primary" type="submit">Criar</button>
</div>
</form>
</div>
</dialog>
<!-- Modal detalhe do lead -->
<dialog id="modal-lead">
<div class="modal-body" id="lead-detalhe"></div>
</dialog>
<div id="toast" class="toast hidden"></div>
<script src="/js/api.js"></script>
<script src="/js/captura.js"></script>
<script src="/js/tratamento.js"></script>
<script src="/js/app.js"></script>
</body>
</html>