Files
chatc2/src/public/settings.html
T
AyronSantos d9e195d924 Foto automática, boleto na conversa e novo Dashboard
- Foto do cliente atualizada automaticamente ao abrir a conversa (remove botão manual)
- Exibe CLI_FOTO na tela de Dados do Cliente; matrícula com conversão robusta
- Envio de boleto na conversa (checkbox em Configurações > Empresa): lista títulos em aberto e envia texto com linha digitável + PIX ao cliente
- Novo Dashboard (gerente): conversas (abertas/não atendidas/não atribuídas/pendentes), atendentes disponíveis/desconectados e heatmap de tráfego
- Presença de atendentes em memória + ping periódico do chat/dashboard

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 15:41:09 -03:00

941 lines
50 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configurações - Chatc2</title>
<link rel="stylesheet" href="/css/main.css">
<style>
body { background:#f3f4f6; display:flex; min-height:100vh; }
.sidebar-nav .admin-only { display:none; }
.main { flex:1; display:flex; flex-direction:column; min-width:0; }
.container { flex:1; padding:24px; overflow-y:auto; }
.tabs { display:flex; margin-bottom:24px; background:#fff; border-radius:12px; overflow:hidden; box-shadow:0 1px 3px rgba(0,0,0,0.08); border:1px solid #f3f4f6; }
.tabs button { flex:1; padding:13px 16px; border:none; background:#fff; font-size:14px; cursor:pointer; border-bottom:2px solid transparent; transition:all .15s; color:#6b7280; font-weight:500; }
.tabs button:hover { background:#f9fafb; color:#374151; }
.tabs button.active { border-bottom-color:#667eea; color:#667eea; font-weight:700; background:#fafbff; }
.tab-content { display:none; }
.tab-content.active { display:block; }
.form-group { margin-bottom:16px; }
.form-group label { display:block; font-size:13px; font-weight:600; color:#374151; margin-bottom:5px; }
.form-group input, .form-group select, .form-group textarea { width:100%; padding:10px 14px; border:2px solid #e5e7eb; border-radius:8px; font-size:14px; outline:none; background:#f9fafb; color:#111827; transition:all .15s; font-family:inherit; }
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color:#667eea; background:#fff; box-shadow:0 0 0 3px rgba(102,126,234,0.1); }
.form-group textarea { min-height:80px; resize:vertical; }
.form-group .toggle { display:flex; align-items:center; gap:8px; }
.form-group .toggle input { width:auto; }
.btn { display:inline-flex; align-items:center; gap:6px; padding:10px 20px; border:none; border-radius:8px; font-size:14px; font-weight:600; cursor:pointer; transition:all .15s; }
.btn-primary { background:#667eea; color:#fff; }
.btn-primary:hover { background:#5a67d8; }
.btn-danger { background:#ef4444; color:#fff; }
.btn-danger:hover { background:#dc2626; }
.btn-sm { padding:6px 12px; font-size:12px; border-radius:6px; }
.badge { display:inline-block; padding:2px 9px; border-radius:10px; font-size:11px; font-weight:600; }
.badge-success { background:#d1fae5; color:#065f46; }
.badge-danger { background:#fef2f2; color:#991b1b; }
.user-info { display:flex; align-items:center; gap:14px; }
.user-name { font-size:14px; font-weight:500; color:#374151; }
.status-badge { display:inline-flex; align-items:center; gap:5px; padding:4px 12px; border-radius:20px; font-size:12px; font-weight:600; }
.status-badge.online { background:#d1fae5; color:#065f46; }
/* Responsivo: abas rolam horizontalmente no celular */
@media (max-width: 768px) {
.tabs { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tabs button { flex: 0 0 auto; white-space: nowrap; padding: 12px 14px; font-size: 13px; }
}
</style>
<link rel="stylesheet" href="/css/dark-mode.css">
<script>function darkModeToggle(){var e=document.body;if(!e)return;var a=localStorage.getItem('chatc2_dark_mode')!=='true';e.classList.toggle('dark-mode',a);localStorage.setItem('chatc2_dark_mode',a?'true':'false');document.querySelectorAll('.dark-mode-toggle').forEach(function(b){b.innerHTML=a?'☀️ Claro':'🌙 Escuro'});}
window.darkModeApply=function(a){var e=document.body;if(!e)return;e.classList.toggle('dark-mode',a);localStorage.setItem('chatc2_dark_mode',a?'true':'false');document.querySelectorAll('.dark-mode-toggle').forEach(function(b){b.innerHTML=a?'☀️ Claro':'🌙 Escuro'});};
window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')==='true';};</script>
</head>
<body>
<aside class="sidebar">
<div class="sidebar-brand">
<div class="logo">C2</div>
<div><h2>Chatc2</h2><span id="sidebarAlias">-</span></div>
</div>
<nav class="sidebar-nav">
<div class="nav-label">Principal</div>
<a href="#" id="navDashboard"><span class="icon">📊</span> Dashboard</a>
<a href="#" id="navClientsSide"><span class="icon">👥</span> Clientes</a>
<a href="#" id="navChat"><span class="icon">💬</span> Conversas</a>
<div class="nav-label admin-only" id="adminLabelSet">Administrador</div>
<a href="#" class="active admin-only" id="navConfig"><span class="icon">⚙️</span> Configurações</a>
<a href="#" id="navRoutesSet" class="admin-only"><span class="icon">📡</span> Rotas</a>
</nav>
<div class="sidebar-footer">
<button class="dark-mode-toggle" onclick="darkModeToggle()" style="width:100%;margin-bottom:8px;padding:8px">🌙 Escuro</button>
<a onclick="logout()"><span class="icon">🚪</span> Sair</a></div>
</aside>
<div class="main">
<div class="topbar"><span class="topbar-title">⚙️ Configurações</span></div>
<div class="container">
<div class="tabs">
<button class="active" onclick="ativarAba('equipe',this)">👥 Equipe</button>
<button onclick="ativarAba('fluxo',this)">📋 Fluxo</button>
<button onclick="ativarAba('empresa',this)">🏢 Empresa</button>
<button onclick="ativarAba('etiquetas',this)">🏷️ Etiquetas</button>
<button onclick="ativarAba('conexao',this)">📱 Conexão</button>
</div>
<!-- Aba Fluxo -->
<div class="tab-content" id="tabFluxo">
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
<h3 style="margin:0">📋 Fluxo de Atendimento</h3>
</div>
<p style="color:#6b7280;font-size:13px;margin-bottom:16px">
Configure os submenus que aparecem quando o cliente seleciona uma equipe.
As opções iniciais são sempre as <strong>Equipes</strong> cadastradas,
com a opção de <strong>Segunda via de Boleto</strong> no final.
</p>
<div id="menusList"><p style="color:#9ca3af">Carregando...</p></div>
</div>
<div class="card" id="cardResolucao">
<h3 style="margin:0 0 8px">🧩 Fluxo de Resolução</h3>
<p style="color:#6b7280;font-size:13px;margin-bottom:16px">
Defina os motivos de atendimento e como o atendente registra a resolução ao finalizar a conversa.
</p>
<h4 style="margin:8px 0">Motivo do Atendimento</h4>
<div style="display:flex;gap:8px;margin-bottom:8px">
<input type="text" id="novoMotivo" placeholder="Cadastrar novo motivo..." style="flex:1;padding:8px;border:2px solid #e5e7eb;border-radius:8px;font-size:13px" onkeydown="if(event.key==='Enter')adicionarMotivo()">
<button class="btn btn-primary btn-sm" onclick="adicionarMotivo()">+ Adicionar</button>
</div>
<div id="motivosList" style="margin-bottom:12px"><p style="color:#9ca3af;font-size:13px">Carregando...</p></div>
<div class="form-group"><div class="toggle"><input type="checkbox" id="flgMotivoVis"> <label for="flgMotivoVis">Visualizar Motivo na tela de conversa</label></div></div>
<div class="form-group"><div class="toggle"><input type="checkbox" id="flgMotivoObr"> <label for="flgMotivoObr">Obrigatório preencher (só finaliza com motivo)</label></div></div>
<h4 style="margin:16px 0 8px">Resolução do Atendimento</h4>
<div class="form-group"><div class="toggle"><input type="checkbox" id="flgResolVis"> <label for="flgResolVis">Visualizar Resolução na tela de conversa</label></div></div>
<div class="form-group"><div class="toggle"><input type="checkbox" id="flgResolObr"> <label for="flgResolObr">Obrigatório preencher (só finaliza com resolução)</label></div></div>
<button class="btn btn-primary" onclick="salvarResolucao()">💾 Salvar Fluxo de Resolução</button>
</div>
</div>
<!-- Aba Equipe -->
<div class="tab-content active" id="tabEquipe">
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
<h3 style="margin:0">Equipes</h3>
<button class="btn btn-primary btn-sm" onclick="mostrarModalEquipe()">+ Nova Equipe</button>
</div>
<div id="equipesList"><p style="color:#9ca3af">Carregando...</p></div>
</div>
<div class="card">
<h3>Usuários da Empresa</h3>
<div id="usuariosList"><p style="color:#9ca3af">Carregando...</p></div>
</div>
</div>
<!-- Aba Empresa -->
<div class="tab-content" id="tabEmpresa">
<div class="card">
<h3>Configurações da Empresa</h3>
<div id="configForm"><p style="color:#9ca3af">Carregando...</p></div>
</div>
</div>
<!-- Aba Etiquetas -->
<div class="tab-content" id="tabEtiquetas">
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
<h3 style="margin:0">Etiquetas</h3>
<button class="btn btn-primary btn-sm" onclick="mostrarModalEtiqueta()">+ Nova Etiqueta</button>
</div>
<div id="etiquetasList"><p style="color:#9ca3af">Carregando...</p></div>
</div>
</div>
<!-- Aba Conexão -->
<div class="tab-content" id="tabConexao">
<div class="card">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px">
<h3 style="margin:0">📱 Conexões WhatsApp</h3>
<button class="btn btn-primary btn-sm" onclick="mostrarModalConexao()">+ Nova Conexão</button>
</div>
<div id="conexoesList"><p style="color:#9ca3af">Carregando...</p></div>
</div>
</div>
</div>
</div>
<!-- Modal Equipe -->
<div class="modal-overlay" id="modalEquipe" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);align-items:center;justify-content:center;z-index:1000">
<div style="background:#fff;border-radius:12px;padding:24px;width:90%;max-width:400px">
<h3 id="modalEquipeTitle">Nova Equipe</h3>
<input type="hidden" id="editEquipeId">
<div class="form-group"><label>Nome da Equipe</label><input type="text" id="equipeNome" placeholder="Ex: Atendimento"></div>
<div class="form-group"><label>Ordem</label><input type="number" id="equipeOrdem" value="0" min="0" style="width:80px"><span style="font-size:12px;color:#9ca3af;margin-left:8px">(menor = aparece primeiro)</span></div>
<div class="form-group"><label>Membros</label><div id="equipeMembros"></div></div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn" onclick="fecharModal('modalEquipe')" style="background:#f3f4f6">Cancelar</button>
<button class="btn btn-primary" onclick="salvarEquipe()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Etiqueta -->
<div class="modal-overlay" id="modalEtiqueta" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);align-items:center;justify-content:center;z-index:1000">
<div style="background:#fff;border-radius:12px;padding:24px;width:90%;max-width:400px">
<h3 id="modalEtiquetaTitle">Nova Etiqueta</h3>
<input type="hidden" id="editEtiquetaId">
<div class="form-group"><label>Nome</label><input type="text" id="etiquetaNome" placeholder="Ex: Cliente VIP"></div>
<div class="form-group"><label>Cor</label><input type="color" id="etiquetaCor" value="#667eea"></div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn" onclick="fecharModal('modalEtiqueta')" style="background:#f3f4f6">Cancelar</button>
<button class="btn btn-primary" onclick="salvarEtiqueta()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Menu -->
<div class="modal-overlay" id="modalMenu" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);align-items:center;justify-content:center;z-index:1000">
<div style="background:#fff;border-radius:12px;padding:24px;width:90%;max-width:500px">
<h3 id="modalMenuTitle">Novo Submenu</h3>
<input type="hidden" id="editMenuId">
<input type="hidden" id="editMenuEquipeId">
<input type="hidden" id="editMenuPaiId">
<div class="form-group" id="menuEquipeGroup"><label>Equipe</label><select id="menuEquipe"><option value="">Selecione...</option></select></div>
<div class="form-group"><label>Título</label><input type="text" id="menuTitulo" placeholder="Ex: Alteração Cadastral"></div>
<div class="form-group">
<label>Tipo</label>
<select id="menuTipo" onchange="mudarTipoMenu()">
<option value="M">📂 Submenu (mostra mais opções)</option>
<option value="T">💬 Texto (envia mensagem fixa)</option>
<option value="R">⚙️ Ação (executa alteração via API)</option>
</select>
</div>
<div class="form-group" id="menuTextoGroup" style="display:none"><label>Texto a ser enviado</label><textarea id="menuTexto" rows="3" placeholder="Digite a mensagem..."></textarea></div>
<div class="form-group" id="menuRotaGroup" style="display:none">
<label>Ação (Rota)</label>
<select id="menuAcaoRota">
<option value="">Selecione uma ação...</option>
<option value="listar_carnes">📄 Listar carnês / Boleto</option>
<option value="alterar_email">📧 Alterar E-mail</option>
<option value="alterar_celular">📱 Alterar Celular</option>
<option value="alterar_endereco">🏠 Alterar Endereço</option>
<option value="info_cliente">️ Mostrar dados do cliente</option>
</select>
<div style="margin-top:8px"><label>Pergunta ao cliente (se precisar de dados)</label>
<textarea id="menuAcaoPrompt" rows="2" placeholder="Ex: Informe seu novo e-mail:" style="width:100%;padding:10px;border:2px solid #e5e7eb;border-radius:8px;font-size:13px;resize:vertical"></textarea></div>
</div>
<div class="form-group"><label>Ordem</label><input type="number" id="menuOrdem" value="0" min="0" style="width:80px"></div>
<div class="form-group"><label>Subordinado a (opcional)</label><select id="menuPai"><option value="">Nenhum (raiz da equipe)</option></select></div>
<div class="form-group"><label>Etiquetas (adicionar ao selecionar)</label><div id="menuEtiquetasContainer" style="display:flex;flex-wrap:wrap;gap:6px;padding:6px;border:1px solid #e5e7eb;border-radius:8px;min-height:36px"></div></div>
<div style="display:flex;gap:8px;justify-content:flex-end;margin-top:12px">
<button class="btn" onclick="fecharModal('modalMenu')" style="background:#f3f4f6">Cancelar</button>
<button class="btn btn-primary" onclick="salvarMenu()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Conexão -->
<div class="modal-overlay" id="modalConexao" style="display:none;position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);align-items:center;justify-content:center;z-index:1000">
<div style="background:#fff;border-radius:12px;padding:24px;width:90%;max-width:500px">
<h3 id="modalConexaoTitle">Nova Conexão WhatsApp</h3>
<input type="hidden" id="editConexaoId">
<div class="form-group"><label>Nome da Instância</label><input type="text" id="conNome" placeholder="Ex: WhatsApp Comercial"></div>
<div class="form-group"><label>URL Evolution API</label><input type="text" id="conUrl" value="https://evoatende.c2sistemas.com.br" placeholder="https://evoatende.c2sistemas.com.br"></div>
<div class="form-group"><label>API Key</label><input type="text" id="conApiKey" placeholder="API Key"></div>
<div class="form-group"><label>Nome da Instância (Evolution)</label><input type="text" id="conInstance" placeholder="Ex: empresa1-wpp"></div>
<div style="display:flex;gap:8px;justify-content:flex-end">
<button class="btn" onclick="fecharModal('modalConexao')" style="background:#f3f4f6">Cancelar</button>
<button class="btn btn-primary" onclick="salvarConexao()" id="btnSalvarConexao">Conectar</button>
</div>
</div>
</div>
<script>
(function(){
'use strict';
const token = localStorage.getItem('chatc2_token');
const pathParts = window.location.pathname.split('/');
const alias = pathParts[2] || localStorage.getItem('chatc2_alias') || 'lajedo';
localStorage.setItem('chatc2_alias', alias);
const user = JSON.parse(localStorage.getItem('chatc2_user') || '{}');
const empresaId = user.empresas?.[0] || 1;
if (!token) { window.location.href = '/app/' + alias + '/login'; return; }
document.getElementById('sidebarAlias').textContent = alias;
document.getElementById("navDashboard").onclick = function(e) { e.preventDefault(); window.location.href = "/app/" + alias + "/dashboard"; };
document.getElementById("navChat").onclick = function(e) { e.preventDefault(); window.location.href = "/app/" + alias + "/company/" + empresaId + "/conversation/0"; };
document.getElementById("navConfig").onclick = function(e) { e.preventDefault(); window.location.href = "/app/" + alias + "/settings"; };
var navClientsSide = document.getElementById("navClientsSide");
if (navClientsSide) navClientsSide.onclick = function(e) { e.preventDefault(); window.location.href = "/app/" + alias + "/clients"; };
var navRoutesSet = document.getElementById("navRoutesSet");
if (navRoutesSet) navRoutesSet.onclick = function(e) { e.preventDefault(); window.location.href = "/app/" + alias + "/routes"; };
var tc = user.tipoChat || "A";
if (tc === "G") {
var admins = document.querySelectorAll(".admin-only");
for (var i = 0; i < admins.length; i++) admins[i].style.display = "";
}
window.logout = function() {
['chatc2_token','chatc2_alias','chatc2_user'].forEach(function(k) { localStorage.removeItem(k); });
window.location.href = '/app/' + alias + '/login';
};
function api(path, opts) {
return fetch('/api/' + alias + path, Object.assign({
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }
}, opts)).then(function(r) { return r.json(); });
}
// ===== ABAS =====
window.ativarAba = function(aba, btn) {
document.querySelectorAll('.tabs button').forEach(function(b) { b.classList.remove('active'); });
document.querySelectorAll('.tab-content').forEach(function(t) { t.classList.remove('active'); });
btn.classList.add('active');
document.getElementById('tab' + aba.charAt(0).toUpperCase() + aba.slice(1)).classList.add('active');
};
// ===== EQUIPES =====
async function carregarEquipes() {
var data = await api('/teams?empresaId=' + empresaId);
var div = document.getElementById('equipesList');
if (!data.success || !data.data || data.data.length === 0) {
div.innerHTML = '<p style="color:#9ca3af">Nenhuma equipe cadastrada</p>';
return;
}
div.innerHTML = '<table><thead><tr><th>Ordem</th><th>Nome</th><th>Membros</th><th>Ações</th></tr></thead><tbody>' +
data.data.map(function(eq) {
var membros = (eq.membros || []).map(function(m) { return m.nome; }).join(', ') || '-';
return '<tr><td>' + (eq.ordem || 0) + '</td><td><strong>' + eq.nome + '</strong></td><td>' + membros + '</td><td>' +
'<button class="btn btn-sm" onclick="editarEquipe(' + eq.id + ',' + (eq.ordem || 0) + ',\'' + eq.nome.replace(/'/g,"\\'") + '\')" style="background:#f3f4f6;margin-right:4px">✏️</button>' +
'<button class="btn btn-sm btn-danger" onclick="excluirEquipe(' + eq.id + ')">🗑️</button></td></tr>';
}).join('') + '</tbody></table>';
}
window.mostrarModalEquipe = function() {
document.getElementById('editEquipeId').value = '';
document.getElementById('equipeOrdem').value = 0;
document.getElementById('equipeNome').value = '';
document.getElementById('modalEquipeTitle').textContent = 'Nova Equipe';
carregarUsuariosCheckbox();
document.getElementById('modalEquipe').style.display = 'flex';
};
window.editarEquipe = function(id, ordem, nome) {
document.getElementById('editEquipeId').value = id;
document.getElementById('equipeOrdem').value = ordem;
document.getElementById('equipeNome').value = nome;
document.getElementById('modalEquipeTitle').textContent = 'Editar Equipe';
carregarUsuariosCheckbox(id);
document.getElementById('modalEquipe').style.display = 'flex';
};
async function carregarUsuariosCheckbox(equipeId) {
var div = document.getElementById('equipeMembros');
var data = await api('/company/users?empresaId=' + empresaId);
if (!data.success) { div.innerHTML = '<p style="color:#9ca3af">Erro ao carregar</p>'; return; }
var membrosAtuais = [];
if (equipeId) {
var eqData = await api('/teams?empresaId=' + empresaId);
var eq = (eqData.data || []).find(function(e) { return e.id === equipeId; });
if (eq) membrosAtuais = (eq.membros || []).map(function(m) { return m.id; });
}
div.innerHTML = (data.data || []).map(function(u) {
var checked = membrosAtuais.includes(u.id) ? 'checked' : '';
return '<label style="display:flex;align-items:center;gap:8px;padding:6px 0;font-size:13px"><input type="checkbox" value="' + u.id + '" ' + checked + '> ' + u.nome + ' (' + u.login + ')</label>';
}).join('');
}
window.salvarEquipe = async function() {
var id = document.getElementById('editEquipeId').value;
var nome = document.getElementById('equipeNome').value.trim();
var ordem = parseInt(document.getElementById('equipeOrdem').value) || 0;
var membros = Array.from(document.querySelectorAll('#equipeMembros input:checked')).map(function(cb) { return parseInt(cb.value); });
if (!nome) return alert('Informe o nome da equipe');
if (id) {
var r = await api('/teams/' + id, { method: 'PUT', body: JSON.stringify({ nome: nome, ordem: ordem, membros: membros }) });
if (r.success) { fecharModal('modalEquipe'); carregarEquipes(); }
} else {
var r = await api('/teams', { method: 'POST', body: JSON.stringify({ nome: nome, ordem: ordem, membros: membros, empresaId: empresaId }) });
if (r.success) { fecharModal('modalEquipe'); carregarEquipes(); }
}
};
window.excluirEquipe = async function(id) {
if (!confirm('Excluir esta equipe?')) return;
var r = await api('/teams/' + id, { method: 'DELETE' });
if (r.success) carregarEquipes();
};
// ===== USUÁRIOS =====
async function carregarUsuarios() {
var data = await api('/company/users?empresaId=' + empresaId);
var div = document.getElementById('usuariosList');
if (!data.success || !data.data || data.data.length === 0) {
div.innerHTML = '<p style="color:#9ca3af">Nenhum usuário encontrado</p>';
return;
}
div.innerHTML = '<table><thead><tr><th>Nome</th><th>Login</th><th>Tipo Chat</th></tr></thead><tbody>' +
data.data.map(function(u) {
var tipo = u.tipoChat === 'A' ? '<span class="badge badge-success">Atendente</span>' : u.tipoChat === 'G' ? '<span class="badge badge-success">Gerente</span>' : '<span class="badge badge-danger">Bloqueado</span>';
return '<tr><td>' + u.nome + '</td><td>' + u.login + '</td><td>' + tipo + '</td></tr>';
}).join('') + '</tbody></table>';
}
// ===== FLUXO DE ATENDIMENTO (MENUS) =====
async function carregarMenus() {
var [eqData, menuData] = await Promise.all([
api('/teams?empresaId=' + empresaId),
api('/menus?empresaId=' + empresaId)
]);
var div = document.getElementById('menusList');
if (!eqData.success || !eqData.data || eqData.data.length === 0) {
div.innerHTML = '<p style="color:#9ca3af">Crie equipes primeiro para configurar os submenus.</p>';
return;
}
var menusAgrupados = menuData.success ? menuData.data : [];
var html = '';
eqData.data.forEach(function(eq) {
var menusEq = menusAgrupados.find(function(g) { return g.equipeId === eq.id; });
var temMenus = menusEq && menusEq.menus && menusEq.menus.length > 0;
html += '<div style="margin-bottom:16px;border:1px solid #e5e7eb;border-radius:8px;overflow:hidden">';
html += '<div style="background:#f9fafb;padding:10px 14px;font-weight:600;font-size:14px;border-bottom:1px solid #e5e7eb;display:flex;align-items:center;justify-content:space-between">';
html += '<span>👥 ' + eq.nome + '</span>';
html += '<button class="btn btn-sm" onclick="mostrarModalMenu(' + eq.id + ')" style="background:#e0e7ff">+ Submenu</button>';
html += '</div>';
if (temMenus) {
html += '<div style="padding:4px 0">' + montarArvoreHtml(menusEq.menus, 0) + '</div>';
} else {
html += '<p style="padding:12px 14px;color:#9ca3af;font-size:13px;margin:0">Nenhum submenu configurado.</p>';
}
html += '</div>';
});
div.innerHTML = html;
}
function montarArvoreHtml(menus, nivel) {
if (!menus || menus.length === 0) return '';
var html = '<div style="padding-left:' + (nivel * 20 + 8) + 'px">';
menus.forEach(function(m) {
var tipoIcon = m.tipo === 'M' ? '📂' : m.tipo === 'T' ? '💬' : m.tipo === 'R' ? '⚙️' : '❓';
html += '<div style="display:flex;align-items:center;padding:6px 10px;border-bottom:1px solid #f3f4f6">';
html += '<span style="flex:1;font-size:13px">' + tipoIcon + ' ' + m.titulo + ' <span style="color:#9ca3af;font-size:11px">(' + m.tipo + ')</span></span>';
html += '<button class="btn btn-sm" onclick="mostrarModalMenu(' + m.equipeId + ',' + m.id + ')" style="background:#f3f4f6;margin-right:4px;padding:4px 8px;font-size:11px">✏️</button>';
html += '<button class="btn btn-sm" onclick="excluirMenu(' + m.id + ',\'' + m.titulo.replace(/'/g,"\\'") + '\')" style="background:#fef2f2;color:#991b1b;padding:4px 8px;font-size:11px">🗑️</button>';
html += '<button class="btn btn-sm" onclick="mostrarModalMenu(' + m.equipeId + ',null,' + m.id + ')" style="margin-left:4px;padding:4px 8px;font-size:11px"> Sub</button>';
html += '</div>';
if (m.filhos && m.filhos.length > 0) {
html += montarArvoreHtml(m.filhos, nivel + 1);
}
});
html += '</div>';
return html;
}
window.mostrarModalMenu = async function(equipeId, editMenuId, parentMenuId) {
// Carrega equipes para o select
var eqData = await api('/teams?empresaId=' + empresaId);
var selectEq = document.getElementById('menuEquipe');
selectEq.innerHTML = '<option value="">Selecione...</option>';
(eqData.data || []).forEach(function(eq) {
var opt = document.createElement('option');
opt.value = eq.id;
opt.textContent = eq.nome;
if (eq.id === equipeId) opt.selected = true;
selectEq.appendChild(opt);
});
document.getElementById('editMenuId').value = editMenuId || '';
document.getElementById('editMenuEquipeId').value = equipeId || '';
document.getElementById('editMenuPaiId').value = parentMenuId || '';
document.getElementById('menuTitulo').value = '';
document.getElementById('menuTipo').value = 'M';
document.getElementById('menuTexto').value = '';
document.getElementById('menuOrdem').value = 0;
document.getElementById('menuAcaoRota').value = '';
document.getElementById('menuAcaoPrompt').value = '';
document.getElementById('menuTextoGroup').style.display = 'none';
document.getElementById('menuRotaGroup').style.display = 'none';
document.getElementById('menuEquipeGroup').style.display = parentMenuId ? 'none' : 'block';
// Carrega menus pai disponíveis
var selectPai = document.getElementById('menuPai');
selectPai.innerHTML = '<option value="">Nenhum (raiz da equipe)</option>';
if (!parentMenuId) {
var flatData = await api('/menus/flat?empresaId=' + empresaId + '&equipeId=' + equipeId);
if (flatData.success && flatData.data) {
flatData.data.forEach(function(m) {
if (m.id !== editMenuId) {
var opt = document.createElement('option');
opt.value = m.id;
opt.textContent = m.titulo;
selectPai.appendChild(opt);
}
});
}
}
// Carrega etiquetas disponiveis como checkboxes
var etiquetasData = await api('/labels?empresaId=' + empresaId);
var etiquetasSelecionadas = editMenuId ? await buscarEtiquetasDoMenu(editMenuId) : [];
if (!parentMenuId) etiquetasSelecionadas = etiquetasSelecionadas || [];
renderEtiquetasCheckbox(etiquetasData, etiquetasSelecionadas);
if (editMenuId) {
// Modo edição: busca dados
document.getElementById('modalMenuTitle').textContent = 'Editar Submenu';
// Como a API retorna agrupado, precisamos buscar os dados de outra forma
// Vamos usar uma chamada direta
var allData = await api('/menus?empresaId=' + empresaId);
if (allData.success) {
var found = null;
allData.data.forEach(function(g) {
function buscar(m, id) {
if (m.id === id) return m;
if (m.filhos) {
for (var f of m.filhos) {
var r = buscar(f, id);
if (r) return r;
}
}
return null;
}
(g.menus || []).forEach(function(m) {
var r = buscar(m, editMenuId);
if (r) found = r;
});
});
if (found) {
document.getElementById('menuTitulo').value = found.titulo || '';
document.getElementById('menuTipo').value = found.tipo || 'M';
document.getElementById('menuTexto').value = found.texto || '';
document.getElementById('menuOrdem').value = found.ordem || 0;
document.getElementById('menuAcaoRota').value = found.acaoRota || '';
document.getElementById('menuAcaoPrompt').value = found.acaoPrompt || '';
if (found.paiId) {
selectPai.value = found.paiId;
}
if (found.tipo === 'T') {
document.getElementById('menuTextoGroup').style.display = 'block';
document.getElementById('menuRotaGroup').style.display = 'none';
} else if (found.tipo === 'R') {
document.getElementById('menuTextoGroup').style.display = 'none';
document.getElementById('menuRotaGroup').style.display = 'block';
}
}
}
} else {
document.getElementById('modalMenuTitle').textContent = 'Novo Submenu';
// Se tem um pai, marca no select
if (parentMenuId) {
selectPai.value = parentMenuId;
}
}
document.getElementById('modalMenu').style.display = 'flex';
};
window.mudarTipoMenu = function() {
var tipo = document.getElementById('menuTipo').value;
document.getElementById('menuTextoGroup').style.display = tipo === 'T' ? 'block' : 'none';
document.getElementById('menuRotaGroup').style.display = tipo === 'R' ? 'block' : 'none';
};
// Busca etiquetas configuradas em um menu (percorre a arvore da API)
async function buscarEtiquetasDoMenu(menuId) {
var allData = await api('/menus?empresaId=' + empresaId);
console.log('[Menu] buscarEtiquetasDoMenu - menuId:', menuId, '| allData:', JSON.stringify(allData).substring(0, 300));
var ids = [];
function percorrer(lista) {
(lista || []).forEach(function(m) {
if (m.id === menuId) {
if (m.etiquetaIds) ids = m.etiquetaIds.split(',').map(function(x){return parseInt(x.trim());}).filter(function(x){return !isNaN(x);});
}
if (m.filhos) percorrer(m.filhos);
});
}
allData.data.forEach(function(g) { percorrer(g.menus); });
return ids;
}
// Renderiza checkboxes de etiquetas no modal
function renderEtiquetasCheckbox(data, selecionadas) {
var container = document.getElementById('menuEtiquetasContainer');
if (!data.success || !data.data || data.data.length === 0) {
container.innerHTML = '<span style="color:#9ca3af;font-size:12px">Nenhuma etiqueta cadastrada</span>';
return;
}
container.innerHTML = data.data.map(function(e) {
var checked = selecionadas.includes(e.id) ? 'checked' : '';
return '<label style="display:flex;align-items:center;gap:4px;padding:4px 8px;background:#f3f4f6;border-radius:6px;font-size:12px;cursor:pointer">' +
'<input type="checkbox" value="' + e.id + '" ' + checked + ' style="accent-color:#667eea"> ' +
'<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:' + (e.cor || '#667eea') + '"></span> ' +
e.nome +
'</label>';
}).join('');
}
window.salvarMenu = async function() {
var editId = document.getElementById('editMenuId').value;
var equipeId = document.getElementById('editMenuEquipeId').value || document.getElementById('menuEquipe').value;
var paiId = document.getElementById('editMenuPaiId').value || document.getElementById('menuPai').value;
var titulo = document.getElementById('menuTitulo').value.trim();
var tipo = document.getElementById('menuTipo').value;
var texto = document.getElementById('menuTexto').value;
var ordem = parseInt(document.getElementById('menuOrdem').value) || 0;
var acaoRota = document.getElementById('menuAcaoRota').value;
var acaoPrompt = document.getElementById('menuAcaoPrompt').value;
// Etiquetas selecionadas
var etiquetaIds = Array.from(document.querySelectorAll('#menuEtiquetasContainer input[type="checkbox"]:checked')).map(function(cb) { return parseInt(cb.value); }).filter(function(v) { return !isNaN(v); }).join(',');
if (!titulo) return alert('Informe o título');
if (!equipeId) return alert('Selecione a equipe');
var body = {
empresaId: empresaId,
equipeId: parseInt(equipeId),
titulo: titulo,
tipo: tipo,
texto: tipo === 'T' ? texto : null,
ordem: ordem,
acaoRota: tipo === 'R' ? acaoRota : null,
acaoPrompt: tipo === 'R' ? acaoPrompt : null,
menuPaiId: paiId ? parseInt(paiId) : null,
etiquetaIds: etiquetaIds || null,
};
console.log('[Menu] Salvando body:', JSON.stringify(body));
if (editId) {
var r = await api('/menus/' + editId, { method: 'PUT', body: JSON.stringify(body) });
console.log('[Menu] Resposta save:', JSON.stringify(r));
if (r.success) { fecharModal('modalMenu'); carregarMenus(); }
else alert('Erro: ' + (r.error || 'desconhecido'));
} else {
var r = await api('/menus', { method: 'POST', body: JSON.stringify(body) });
if (r.success) { fecharModal('modalMenu'); carregarMenus(); }
else alert('Erro: ' + (r.error || 'desconhecido'));
}
};
window.excluirMenu = async function(id, titulo) {
if (!confirm('Excluir o submenu "' + titulo + '" e todos os seus sub-itens?')) return;
var r = await api('/menus/' + id, { method: 'DELETE' });
if (r.success) carregarMenus();
};
// ===== ETIQUETAS =====
async function carregarEtiquetas() {
var data = await api('/labels?empresaId=' + empresaId);
var div = document.getElementById('etiquetasList');
if (!data.success || !data.data || data.data.length === 0) {
div.innerHTML = '<p style="color:#9ca3af">Nenhuma etiqueta cadastrada</p>';
return;
}
div.innerHTML = '<table><thead><tr><th>Nome</th><th>Cor</th><th>Ações</th></tr></thead><tbody>' +
data.data.map(function(l) {
return '<tr><td><strong>' + l.nome + '</strong></td><td><span class="tag" style="background:' + l.cor + '">' + l.cor + '</span></td><td>' +
'<button class="btn btn-sm" onclick="editarEtiqueta(' + l.id + ',\'' + l.nome.replace(/'/g,"\\'") + '\',\'' + l.cor + '\')" style="background:#f3f4f6;margin-right:4px">✏️</button>' +
'<button class="btn btn-sm btn-danger" onclick="excluirEtiqueta(' + l.id + ')">🗑️</button></td></tr>';
}).join('') + '</tbody></table>';
}
window.mostrarModalEtiqueta = function() {
document.getElementById('editEtiquetaId').value = '';
document.getElementById('etiquetaNome').value = '';
document.getElementById('etiquetaCor').value = '#667eea';
document.getElementById('modalEtiquetaTitle').textContent = 'Nova Etiqueta';
document.getElementById('modalEtiqueta').style.display = 'flex';
};
window.editarEtiqueta = function(id, nome, cor) {
document.getElementById('editEtiquetaId').value = id;
document.getElementById('etiquetaNome').value = nome;
document.getElementById('etiquetaCor').value = cor;
document.getElementById('modalEtiquetaTitle').textContent = 'Editar Etiqueta';
document.getElementById('modalEtiqueta').style.display = 'flex';
};
window.salvarEtiqueta = async function() {
var id = document.getElementById('editEtiquetaId').value;
var nome = document.getElementById('etiquetaNome').value.trim();
var cor = document.getElementById('etiquetaCor').value;
if (!nome) return alert('Informe o nome');
if (id) {
var r = await api('/labels/' + id, { method: 'PUT', body: JSON.stringify({ nome: nome, cor: cor }) });
if (r.success) { fecharModal('modalEtiqueta'); carregarEtiquetas(); }
} else {
var r = await api('/labels', { method: 'POST', body: JSON.stringify({ nome: nome, cor: cor }) });
if (r.success) { fecharModal('modalEtiqueta'); carregarEtiquetas(); }
}
};
window.excluirEtiqueta = async function(id) {
if (!confirm('Excluir esta etiqueta?')) return;
var r = await api('/labels/' + id, { method: 'DELETE' });
if (r.success) carregarEtiquetas();
};
// ===== CONFIGURAÇÕES EMPRESA =====
async function carregarConfig() {
var data = await api('/company/config?empresaId=' + empresaId);
var div = document.getElementById('configForm');
if (!data.success) { div.innerHTML = '<p style="color:#ef4444">Erro ao carregar</p>'; return; }
var cfg = data.data;
div.innerHTML =
'<div class="form-group"><label>Instância Padrão</label><select id="cfgInstancia"><option value="">Nenhuma</option></select></div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgFoto" ' + (cfg.fotoCelular === 'S' ? 'checked' : '') + '> <label for="cfgFoto">Atualizar foto do cliente conforme WhatsApp</label></div></div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgSaudacao" ' + (cfg.saudacaoAtiva === 'S' ? 'checked' : '') + '> <label for="cfgSaudacao">Ativar saudação automática</label></div></div>' +
'<div class="form-group"><label>Mensagem de Saudação</label><textarea id="cfgSaudacaoMsg">' + (cfg.saudacaoMensagem || '') + '</textarea></div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgNomeUser" ' + (cfg.enviarNomeUsuario === 'S' ? 'checked' : '') + '> <label for="cfgNomeUser">Mostrar nome do usuário nas mensagens ("Nome: Mensagem")</label></div></div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgEnviarBoleto" ' + (cfg.enviarBoleto === 'S' ? 'checked' : '') + '> <label for="cfgEnviarBoleto">💳 Permitir enviar boleto na conversa (botão "Enviar boleto")</label></div></div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgTriagem" ' + (cfg.triagemAtiva === 'S' ? 'checked' : '') + '> <label for="cfgTriagem">📋 Ativar fluxo de triagem (menu de opções)</label></div></div>' +
'<div class="form-group" id="triagemConfig" style="display:' + (cfg.triagemAtiva === 'S' ? 'block' : 'none') + ';padding:12px;background:#f9fafb;border-radius:8px;margin-bottom:12px">' +
'<div class="form-group"><label>Mensagem de boas-vindas (use {EMPRESA} para o nome)</label><textarea id="cfgTriagemWelcome" rows="2" style="width:100%;padding:10px;border:2px solid #e5e7eb;border-radius:8px;font-size:13px;resize:vertical">' + (cfg.triagemMsgWelcome || '') + '</textarea></div>' +
'<div class="form-group"><label>Mensagem após escolha</label><textarea id="cfgTriagemAfter" rows="2" style="width:100%;padding:10px;border:2px solid #e5e7eb;border-radius:8px;font-size:13px;resize:vertical">' + (cfg.triagemMsgAfter || '') + '</textarea></div>' +
'<div class="form-group"><label>Número da opção "Segunda via de Boleto"</label><input type="text" id="cfgTriagemBoletoNum" value="' + (cfg.triagemBoletoNumero || '0') + '" style="width:100%;padding:10px;border:2px solid #e5e7eb;border-radius:8px;font-size:14px"></div>' +
'</div>' +
'<div class="form-group"><div class="toggle"><input type="checkbox" id="cfgCsat" ' + (cfg.csatAtivo === 'S' ? 'checked' : '') + '> <label for="cfgCsat">Ativar CSAT (Pesquisa de satisfação)</label></div></div>' +
'<div class="form-group"><label>Mensagem CSAT</label><textarea id="cfgCsatMsg">' + (cfg.csatMensagem || '') + '</textarea></div>' +
'<button class="btn btn-primary" onclick="salvarConfig()">💾 Salvar Configurações</button>';
// Evento para toggle da triagem
document.getElementById('cfgTriagem').addEventListener('change', function() {
document.getElementById('triagemConfig').style.display = this.checked ? 'block' : 'none';
});
// Carrega instâncias no select
var instData = await api('/evolution/instances?empresaId=' + empresaId);
if (instData.success && instData.data) {
var select = document.getElementById('cfgInstancia');
instData.data.forEach(function(ins) {
var opt = document.createElement('option');
opt.value = ins.id;
opt.textContent = ins.nome + ' (' + ins.instanceName + ')';
if (ins.id === cfg.instanciaPadraoId) opt.selected = true;
select.appendChild(opt);
});
}
}
window.salvarConfig = async function() {
var data = {
empresaId: empresaId,
instanciaPadraoId: parseInt(document.getElementById('cfgInstancia').value) || null,
fotoCelular: document.getElementById('cfgFoto').checked ? 'S' : 'N',
saudacaoAtiva: document.getElementById('cfgSaudacao').checked ? 'S' : 'N',
saudacaoMensagem: document.getElementById('cfgSaudacaoMsg').value,
enviarNomeUsuario: document.getElementById('cfgNomeUser').checked ? 'S' : 'N',
enviarBoleto: document.getElementById('cfgEnviarBoleto').checked ? 'S' : 'N',
triagemAtiva: document.getElementById('cfgTriagem').checked ? 'S' : 'N',
triagemMsgWelcome: document.getElementById('cfgTriagemWelcome').value,
triagemMsgAfter: document.getElementById('cfgTriagemAfter').value,
triagemBoletoNumero: document.getElementById('cfgTriagemBoletoNum').value,
csatAtivo: document.getElementById('cfgCsat').checked ? 'S' : 'N',
csatMensagem: document.getElementById('cfgCsatMsg').value,
};
var r = await api('/company/config', { method: 'POST', body: JSON.stringify(data) });
if (r.success) alert('Configurações salvas com sucesso!');
};
// O addEventListener do cfgTriagem é adicionado dentro do carregarConfig() após criar o HTML
// ===== FLUXO DE RESOLUÇÃO =====
function escc(s){ return String(s == null ? '' : s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
async function carregarResolucao() {
var md = await api('/motivos?empresaId=' + empresaId);
var div = document.getElementById('motivosList');
if (div && md.success) {
div.innerHTML = (md.data && md.data.length)
? md.data.map(function(m) {
return '<div style="display:flex;justify-content:space-between;align-items:center;padding:6px 10px;border:1px solid #eee;border-radius:6px;margin-bottom:4px">' +
'<span style="font-size:13px">' + escc(m.descricao) + '</span>' +
'<button class="btn btn-sm" style="color:#dc2626;background:none;border:none;cursor:pointer" onclick="removerMotivo(' + m.id + ')">🗑️</button></div>';
}).join('')
: '<p style="color:#9ca3af;font-size:13px">Nenhum motivo cadastrado.</p>';
}
var cd = await api('/company/config?empresaId=' + empresaId);
if (cd.success) {
var s = function(id, v) { var el = document.getElementById(id); if (el) el.checked = v === 'S'; };
s('flgMotivoVis', cd.data.motivoVisualizar);
s('flgMotivoObr', cd.data.motivoObrigatorio);
s('flgResolVis', cd.data.resolucaoVisualizar);
s('flgResolObr', cd.data.resolucaoObrigatorio);
}
}
window.adicionarMotivo = async function() {
var inp = document.getElementById('novoMotivo');
var d = (inp.value || '').trim();
if (!d) return;
var r = await api('/motivos', { method: 'POST', body: JSON.stringify({ descricao: d, empresaId: empresaId }) });
if (r.success) { inp.value = ''; carregarResolucao(); } else alert(r.error || 'Erro ao adicionar motivo');
};
window.removerMotivo = async function(id) {
if (!confirm('Remover este motivo?')) return;
var r = await api('/motivos/' + id, { method: 'DELETE' });
if (r.success) carregarResolucao(); else alert(r.error || 'Erro ao remover');
};
window.salvarResolucao = async function() {
var body = {
empresaId: empresaId,
motivoVisualizar: document.getElementById('flgMotivoVis').checked ? 'S' : 'N',
motivoObrigatorio: document.getElementById('flgMotivoObr').checked ? 'S' : 'N',
resolucaoVisualizar: document.getElementById('flgResolVis').checked ? 'S' : 'N',
resolucaoObrigatorio: document.getElementById('flgResolObr').checked ? 'S' : 'N',
};
var r = await api('/company/resolucao-config', { method: 'POST', body: JSON.stringify(body) });
if (r.success) alert('Fluxo de Resolução salvo!'); else alert(r.error || 'Erro ao salvar');
};
// ===== CONEXÕES =====
async function carregarConexoes() {
var data = await api('/evolution/instances?empresaId=' + empresaId);
var div = document.getElementById('conexoesList');
if (!data.success || !data.data || data.data.length === 0) {
div.innerHTML = '<p style="color:#9ca3af">Nenhuma conexão cadastrada</p>';
return;
}
div.innerHTML = '<table><thead><tr><th>Nome</th><th>Instância</th><th>URL</th><th>Status</th><th>Ações</th></tr></thead><tbody>' +
data.data.map(function(ins) {
var status = ins.status === 'A' ? '<span class="badge badge-success">✅ Conectado</span>' :
ins.status === 'C' ? '<span class="badge badge-success">🟡 Conectando</span>' :
ins.status === 'D' ? '<span class="badge badge-danger">⛔ Desconectado</span>' : '<span class="badge badge-danger">❌ ' + ins.status + '</span>';
var nomeEsc = (ins.nome || '').replace(/'/g, "\\'");
return '<tr><td><strong>' + ins.nome + '</strong></td><td>' + ins.instanceName + '</td><td style="font-size:11px;max-width:150px;overflow:hidden;text-overflow:ellipsis">' + ins.url + '</td><td>' + status + '</td><td>' +
'<button class="btn btn-sm" onclick="editarConexao(' + ins.id + ')" style="background:#f3f4f6;margin-right:4px">✏️</button>' +
'<button class="btn btn-sm" onclick="excluirConexao(' + ins.id + ',\'' + nomeEsc + '\')" style="background:#fef2f2;color:#991b1b;margin-right:4px">🗑️</button>' +
'<button class="btn btn-sm" onclick="gerarQRCode(' + ins.id + ')" style="background:#f3f4f6">📱 QR Code</button></td></tr>';
}).join('') + '</tbody></table>';
}
window.mostrarModalConexao = function() {
document.getElementById('editConexaoId').value = '';
document.getElementById('conNome').value = '';
document.getElementById('conUrl').value = 'https://evoatende.c2sistemas.com.br';
document.getElementById('conApiKey').value = '';
document.getElementById('conInstance').value = '';
document.getElementById('modalConexaoTitle').textContent = 'Nova Conexão WhatsApp';
document.getElementById('btnSalvarConexao').textContent = 'Conectar';
document.getElementById('modalConexao').style.display = 'flex';
};
window.gerarQRCode = async function(id) {
var r = await api('/evolution/qrcode/' + id, { method: 'POST' });
if (r.success && r.data && (r.data.qrcode || r.data.base64)) {
var qr = r.data.qrcode || r.data.base64;
var imgSrc = qr.startsWith('data:') ? qr : 'data:image/png;base64,' + qr;
var w = window.open('', '_blank', 'width=400,height=500');
w.document.write('<html><head><title>QR Code</title></head><body style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;font-family:sans-serif">' +
'<h2 style="margin-bottom:20px">Escaneie o QR Code</h2>' +
'<img src="' + imgSrc + '" style="max-width:300px">' +
'<p style="margin-top:20px;color:#6b7280">Abra o WhatsApp no celular e escaneie</p></body></html>');
} else {
alert('Erro ao gerar QR Code: ' + JSON.stringify(r.data));
}
};
// ===== EDITAR CONEXÃO =====
window.editarConexao = async function(id) {
// Busca dados atuais da API
var data = await api('/evolution/instances?empresaId=' + empresaId);
if (!data.success) return alert('Erro ao carregar dados');
var ins = data.data.find(function(i) { return i.id === id; });
if (!ins) return alert('Instância não encontrada');
document.getElementById('editConexaoId').value = id;
document.getElementById('conNome').value = ins.nome;
document.getElementById('conUrl').value = ins.url;
document.getElementById('conApiKey').value = ins.apiKey;
document.getElementById('conInstance').value = ins.instanceName;
document.getElementById('modalConexaoTitle').textContent = 'Editar Conexão WhatsApp';
document.getElementById('btnSalvarConexao').textContent = 'Salvar';
document.getElementById('modalConexao').style.display = 'flex';
};
// ===== EXCLUIR CONEXÃO =====
window.excluirConexao = async function(id, nome) {
if (!confirm('Excluir a conexão "' + nome + '"?')) return;
var r = await api('/evolution/instances/' + id, { method: 'DELETE' });
if (r.success) {
carregarConexoes();
} else {
alert('Erro: ' + r.error);
}
};
window.salvarConexao = async function() {
var editId = document.getElementById('editConexaoId').value;
var data = {
INS_NOME: document.getElementById('conNome').value.trim(),
INS_URL: document.getElementById('conUrl').value.trim(),
INS_API_KEY: document.getElementById('conApiKey').value.trim(),
INS_INSTANCE_NAME: document.getElementById('conInstance').value.trim(),
};
if (!data.INS_NOME || !data.INS_URL || !data.INS_API_KEY || !data.INS_INSTANCE_NAME)
return alert('Preencha todos os campos');
var r;
if (editId) {
// Atualizar
r = await api('/evolution/instances/' + editId, { method: 'PUT', body: JSON.stringify(data) });
} else {
// Criar nova
data.INS_EMPRESA_ID = empresaId;
r = await api('/evolution/connect', { method: 'POST', body: JSON.stringify(data) });
}
if (r.success) {
fecharModal('modalConexao');
document.getElementById('editConexaoId').value = '';
document.getElementById('modalConexaoTitle').textContent = 'Nova Conexão WhatsApp';
document.getElementById('btnSalvarConexao').textContent = 'Conectar';
carregarConexoes();
if (!editId && r.data && r.data.id) gerarQRCode(r.data.id);
} else {
alert('Erro: ' + r.error);
}
};
// ===== MODAIS =====
window.fecharModal = function(id) { document.getElementById(id).style.display = 'none'; };
// Fechar modal ao clicar fora
document.querySelectorAll('.modal-overlay').forEach(function(el) {
el.addEventListener('click', function(e) { if (e.target === this) this.style.display = 'none'; });
});
// ===== INICIAR =====
carregarEquipes();
carregarUsuarios();
carregarMenus();
carregarEtiquetas();
carregarConexoes();
carregarConfig();
carregarResolucao();
})();
</script>
<script src="/js/dark-mode.js"></script>
</body>
</html>