Files
chatc2/src/public/settings.html
T

1088 lines
57 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:var(--surface-3); 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 var(--surface-3); }
.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:var(--text-muted); font-weight:500; }
.tabs button:hover { background:var(--surface-2); color:var(--text-secondary); }
.tabs button.active { border-bottom-color:var(--primary); color:var(--primary); 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:var(--text-secondary); margin-bottom:5px; }
.form-group input, .form-group select, .form-group textarea { width:100%; padding:10px 14px; border:2px solid var(--border); border-radius:8px; font-size:14px; outline:none; background:var(--surface-2); color:var(--text-primary); transition:all .15s; font-family:inherit; }
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color:var(--primary); 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:var(--primary); color:#fff; }
.btn-primary:hover { background:var(--primary-dark); }
.btn-secondary {
display: inline-flex; align-items: center; gap: 6px;
padding: 10px 20px; border: 1px solid var(--border);
border-radius: 8px; font-size: 14px; font-weight: 600;
cursor: pointer; transition: all .15s;
background: var(--surface); color: var(--text-secondary);
}
.btn-secondary:hover { background: var(--surface-2); border-color: #d1d5db; }
.btn-danger { background:var(--danger); color:#fff; }
.btn-danger:hover { background:#dc2626; }
.btn-danger:disabled { opacity:0.5; cursor:not-allowed; background:var(--danger); }
.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:var(--success-bg); color:var(--success-text); }
.badge-danger { background:var(--danger-bg); color:var(--danger-text); }
.user-info { display:flex; align-items:center; gap:14px; }
.user-name { font-size:14px; font-weight:500; color:var(--text-secondary); }
.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:var(--success-bg); color:var(--success-text); }
.feedback-msg {
padding: 8px 14px; border-radius: 6px; font-size: 13px;
margin-bottom: 8px; display: none;
}
.feedback-msg.sucesso { background: var(--success-bg); color: var(--success-text); display: block; }
.feedback-msg.erro { background: var(--danger-bg); color: var(--danger-text); display: block; }
/* 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(){var m=localStorage.getItem("chatc2_dark_mode_manual");var d=m!==null?m==="true":window.matchMedia("(prefers-color-scheme: dark)").matches;if(d)document.documentElement.className="dark"})()</script>
</head>
<body>
<aside class="sidebar" role="navigation" aria-label="Navegação principal">
<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" aria-label="Alternar modo escuro">🌙 Escuro</button>
<a onclick="logout()" aria-label="Sair do sistema"><span class="icon">🚪</span> Sair</a></div>
</aside>
<main class="main" id="main-content">
<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:var(--text-muted);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:var(--text-faint)">Carregando...</p></div>
</div>
<div class="card" id="cardResolucao">
<h3 style="margin:0 0 8px">🧩 Fluxo de Resolução</h3>
<p style="color:var(--text-muted);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:var(--text-faint);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>
<div id="resolucaoFeedback" class="feedback-msg"></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:var(--text-faint)">Carregando...</p></div>
</div>
<div class="card">
<h3>Usuários da Empresa</h3>
<div id="usuariosList"><p style="color:var(--text-faint)">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:var(--text-faint)">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:var(--text-faint)">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:var(--text-faint)">Carregando...</p></div>
</div>
</div>
</div>
</div>
<!-- Modal Equipe -->
<div class="modal-overlay" id="modalEquipe">
<div class="modal-box" style="max-width:480px">
<h3 id="modalEquipeTitle">Nova Equipe</h3>
<input type="hidden" id="editEquipeId">
<div class="form-group">
<label for="equipeNome">Nome da Equipe</label>
<input type="text" id="equipeNome" placeholder="Ex: Atendimento">
</div>
<div class="form-group">
<label for="equipeOrdem">Ordem</label>
<div style="display:flex;align-items:center;gap:8px">
<input type="number" id="equipeOrdem" value="0" min="0" style="width:80px">
<span style="font-size:12px;color:var(--text-faint)">Menor = aparece primeiro</span>
</div>
</div>
<div class="form-group">
<label for="equipeMensagem">Mensagem automática</label>
<textarea id="equipeMensagem" rows="2" placeholder="Mensagem enviada quando o cliente escolhe esta equipe..."></textarea>
<span style="font-size:11px;color:var(--text-faint);margin-top:4px;display:block">Opcional. Se preenchida, será enviada ao cliente após escolher esta opção.</span>
</div>
<div class="form-group">
<label for="equipeEtiqueta">Etiqueta automática</label>
<select id="equipeEtiqueta"><option value="">Nenhuma</option></select>
<span style="font-size:11px;color:var(--text-faint);margin-top:4px;display:block">Opcional. Etiqueta adicionada automaticamente à conversa.</span>
</div>
<div class="form-group">
<label>Membros</label>
<div id="equipeMembros"></div>
</div>
<div id="equipeFeedback" class="feedback-msg"></div>
<div class="modal-footer">
<button class="btn-secondary" onclick="fecharModal('modalEquipe')">Cancelar</button>
<button class="btn-primary" onclick="salvarEquipe()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Etiqueta -->
<div class="modal-overlay" id="modalEtiqueta">
<div class="modal-box" style="max-width:400px">
<h3 id="modalEtiquetaTitle">Nova Etiqueta</h3>
<input type="hidden" id="editEtiquetaId">
<div class="form-group"><label for="etiquetaNome">Nome</label><input type="text" id="etiquetaNome" placeholder="Ex: Cliente VIP"></div>
<div class="form-group"><label for="etiquetaCor">Cor</label><input type="color" id="etiquetaCor" value="#667eea"></div>
<div id="etiquetaFeedback" class="feedback-msg"></div>
<div class="modal-footer">
<button class="btn-secondary" onclick="fecharModal('modalEtiqueta')">Cancelar</button>
<button class="btn-primary" onclick="salvarEtiqueta()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Menu -->
<div class="modal-overlay" id="modalMenu">
<div class="modal-box" style="max-width:520px">
<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 for="menuEquipe">Equipe</label><select id="menuEquipe"><option value="">Selecione...</option></select></div>
<div class="form-group"><label for="menuTitulo">Título</label><input type="text" id="menuTitulo" placeholder="Ex: Alteração Cadastral"></div>
<div class="form-group"><label for="menuTipo">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 for="menuTexto">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 for="menuAcaoRota">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 for="menuAcaoPrompt">Pergunta ao cliente (se precisar de dados)</label>
<textarea id="menuAcaoPrompt" rows="2" placeholder="Ex: Informe seu novo e-mail:"></textarea></div>
</div>
<div class="form-group"><label for="menuOrdem">Ordem</label><input type="number" id="menuOrdem" value="0" min="0" style="width:80px"></div>
<div class="form-group"><label for="menuPai">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 var(--border);border-radius:8px;min-height:36px"></div></div>
<div id="menuFeedback" class="feedback-msg"></div>
<div class="modal-footer">
<button class="btn-secondary" onclick="fecharModal('modalMenu')">Cancelar</button>
<button class="btn-primary" onclick="salvarMenu()">Salvar</button>
</div>
</div>
</div>
<!-- Modal Conexão -->
<div class="modal-overlay" id="modalConexao">
<div class="modal-box" style="max-width:520px">
<h3 id="modalConexaoTitle">Nova Conexão WhatsApp</h3>
<input type="hidden" id="editConexaoId">
<div class="form-group"><label for="conNome">Nome da Instância</label><input type="text" id="conNome" placeholder="Ex: WhatsApp Comercial"></div>
<div class="form-group"><label for="conUrl">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 for="conApiKey">API Key</label><input type="text" id="conApiKey" placeholder="API Key"></div>
<div class="form-group"><label for="conInstance">Nome da Instância (Evolution)</label><input type="text" id="conInstance" placeholder="Ex: empresa1-wpp"></div>
<div id="conexaoFeedback" class="feedback-msg"></div>
<div class="modal-footer">
<button class="btn-secondary" onclick="fecharModal('modalConexao')">Cancelar</button>
<button class="btn-primary" onclick="salvarConexao()" id="btnSalvarConexao">Conectar</button>
</div>
</div>
</div>
<script>
(function(){
'use strict';
// Fechar modais com Esc
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
document.querySelectorAll('.modal-overlay.show, .modal.show').forEach(function(m) {
m.classList.remove('show');
});
}
});
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');
};
// ===== MODAL DE CONFIRMAÇÃO =====
let _confirmarCallback = null;
window.mostrarConfirmacao = function(titulo, mensagem, nomeConfirmar, callback) {
document.getElementById('confirmarTitulo').textContent = titulo;
document.getElementById('confirmarMensagem').textContent = mensagem;
document.getElementById('confirmarInput').value = '';
document.getElementById('confirmarInput').placeholder = 'Digite: ' + nomeConfirmar;
document.getElementById('btnConfirmarExclusao').disabled = true;
_confirmarCallback = callback;
_initConfirmModal();
document.getElementById('modalConfirmar').classList.add('show');
document.getElementById('confirmarInput').focus();
};
// Event listeners do modal de confirmação (inicializados após DOM pronto)
function _initConfirmModal() {
var inp = document.getElementById('confirmarInput');
if (!inp || inp._confirmInit) return;
inp._confirmInit = true;
inp.addEventListener('input', function() {
var esperado = this.placeholder.replace('Digite: ', '');
document.getElementById('btnConfirmarExclusao').disabled = this.value.trim() !== esperado;
});
inp.addEventListener('keydown', function(e) {
if (e.key === 'Enter' && !document.getElementById('btnConfirmarExclusao').disabled) {
document.getElementById('btnConfirmarExclusao').click();
}
});
document.getElementById('btnConfirmarExclusao').addEventListener('click', function() {
if (_confirmarCallback) { _confirmarCallback(); _confirmarCallback = null; }
fecharModal('modalConfirmar');
});
}
// ===== 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:var(--text-faint)">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(', ') || '-';
var mensagemEsc = (eq.mensagem || '').replace(/\\/g,'\\\\').replace(/'/g,"\\'");
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,"\\'") + '\',\'' + mensagemEsc + '\',' + (eq.etiquetaId || 'null') + ')" style="background:var(--surface-3);margin-right:4px">✏️</button>' +
'<button class="btn btn-sm btn-danger" onclick="excluirEquipe(' + eq.id + ',\'' + eq.nome.replace(/'/g,"\\'") + '\')">🗑️</button></td></tr>';
}).join('') + '</tbody></table>';
}
window.mostrarModalEquipe = function() {
document.getElementById('editEquipeId').value = '';
document.getElementById('equipeOrdem').value = 0;
document.getElementById('equipeNome').value = '';
document.getElementById('equipeMensagem').value = '';
document.getElementById('equipeEtiqueta').value = '';
document.getElementById('modalEquipeTitle').textContent = 'Nova Equipe';
carregarUsuariosCheckbox();
carregarEtiquetasDropdown();
document.getElementById('modalEquipe').classList.add('show');
};
window.editarEquipe = async function(id, ordem, nome, mensagem, etiquetaId) {
document.getElementById('editEquipeId').value = id;
document.getElementById('equipeOrdem').value = ordem;
document.getElementById('equipeNome').value = nome;
document.getElementById('equipeMensagem').value = mensagem || '';
document.getElementById('equipeEtiqueta').value = etiquetaId || '';
document.getElementById('modalEquipeTitle').textContent = 'Editar Equipe';
await carregarEtiquetasDropdown();
carregarUsuariosCheckbox(id);
document.getElementById('modalEquipe').classList.add('show');
};
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:var(--text-faint)">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('');
}
async function carregarEtiquetasDropdown() {
var sel = document.getElementById('equipeEtiqueta');
var data = await api('/labels?empresaId=' + empresaId);
sel.innerHTML = '<option value="">Nenhuma</option>';
if (data.success && data.data) {
data.data.forEach(function(l) {
sel.innerHTML += '<option value="' + l.id + '">' + l.nome + '</option>';
});
}
}
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); });
var mensagem = document.getElementById('equipeMensagem').value.trim();
var etiquetaId = document.getElementById('equipeEtiqueta').value || null;
if (!nome) { mostrarFeedbackModal('equipeFeedback', 'Informe o nome da equipe', 'erro'); return; }
if (id) {
var r = await api('/teams/' + id, { method: 'PUT', body: JSON.stringify({ nome: nome, ordem: ordem, membros: membros, mensagem: mensagem, etiquetaId: etiquetaId }) });
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, mensagem: mensagem, etiquetaId: etiquetaId }) });
if (r.success) { fecharModal('modalEquipe'); carregarEquipes(); }
}
};
window.excluirEquipe = async function(id, nome) {
mostrarConfirmacao('Excluir Equipe', 'Esta ação não pode ser desfeita. Todos os submenus vinculados serão perdidos.', nome || 'equipe', async function() {
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:var(--text-faint)">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:var(--text-faint)">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 var(--border);border-radius:8px;overflow:hidden">';
html += '<div style="background:var(--surface-2);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:var(--text-faint);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:var(--text-faint);font-size:11px">(' + m.tipo + ')</span></span>';
html += '<button class="btn btn-sm" onclick="mostrarModalMenu(' + m.equipeId + ',' + m.id + ')" style="background:var(--surface-3);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').classList.add('show');
};
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:var(--text-faint);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:var(--surface-3);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) { mostrarFeedbackModal('menuFeedback', 'Informe o título', 'erro'); return; }
if (!equipeId) { mostrarFeedbackModal('menuFeedback', 'Selecione a equipe', 'erro'); return; }
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 mostrarFeedbackModal('menuFeedback', 'Erro: ' + (r.error || 'desconhecido'), 'erro');
} else {
var r = await api('/menus', { method: 'POST', body: JSON.stringify(body) });
if (r.success) { fecharModal('modalMenu'); carregarMenus(); }
else mostrarFeedbackModal('menuFeedback', 'Erro: ' + (r.error || 'desconhecido'), 'erro');
}
};
window.excluirMenu = async function(id, titulo) {
mostrarConfirmacao('Excluir Submenu', 'O submenu "' + titulo + '" e todos os seus sub-itens serão removidos.', titulo, async function() {
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:var(--text-faint)">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:var(--surface-3);margin-right:4px">✏️</button>' +
'<button class="btn btn-sm btn-danger" onclick="excluirEtiqueta(' + l.id + ',\'' + l.nome.replace(/'/g,"\\'") + '\')">🗑️</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').classList.add('show');
};
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').classList.add('show');
};
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) { mostrarFeedbackModal('etiquetaFeedback', 'Informe o nome', 'erro'); return; }
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, nome) {
mostrarConfirmacao('Excluir Etiqueta', 'A etiqueta será removida permanentemente.', nome, async function() {
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:var(--surface-2);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="cfgResolucaoSalvarMSC" ' + (cfg.resolucaoSalvarMSC === 'S' ? 'checked' : '') + '> <label for="cfgResolucaoSalvarMSC">💾 Salvar resolução de atendimento na tabela MENSAGENS_CLIENTES (como aviso)</label></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>' +
'<div id="configFeedback" class="feedback-msg"></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,
resolucaoSalvarMSC: document.getElementById('cfgResolucaoSalvarMSC').checked ? 'S' : 'N',
};
var r = await api('/company/config', { method: 'POST', body: JSON.stringify(data) });
if (r.success) mostrarFeedbackModal('configFeedback', 'Configurações salvas com sucesso!', '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 var(--border);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 + ',\'' + escc(m.descricao).replace(/'/g,"\\'") + '\')">🗑️</button></div>';
}).join('')
: '<p style="color:var(--text-faint);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 mostrarFeedbackResolucao(r.error || 'Erro ao adicionar motivo', 'erro');
};
window.removerMotivo = async function(id, descricao) {
mostrarConfirmacao('Remover Motivo', 'O motivo será removido permanentemente.', descricao, async function() {
var r = await api('/motivos/' + id, { method: 'DELETE' });
if (r.success) carregarResolucao(); else mostrarFeedbackResolucao(r.error || 'Erro ao remover', 'erro');
});
};
function mostrarFeedbackResolucao(msg, tipo) {
var fb = document.getElementById('resolucaoFeedback');
if (!fb) return;
fb.textContent = msg;
fb.className = 'feedback-msg ' + tipo;
setTimeout(function() { fb.className = 'feedback-msg'; }, 4000);
}
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) mostrarFeedbackModal('resolucaoFeedback', 'Fluxo de Resolução salvo!', 'sucesso'); else mostrarFeedbackModal('resolucaoFeedback', r.error || 'Erro ao salvar', 'erro');
};
// ===== 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:var(--text-faint)">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:var(--surface-3);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:var(--surface-3)">📱 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').classList.add('show');
};
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:var(--text-muted)">Abra o WhatsApp no celular e escaneie</p></body></html>');
} else {
mostrarFeedbackModal('conexaoFeedback', 'Erro ao gerar QR Code', 'erro');
}
};
// ===== EDITAR CONEXÃO =====
window.editarConexao = async function(id) {
// Busca dados atuais da API
var data = await api('/evolution/instances?empresaId=' + empresaId);
if (!data.success) { mostrarFeedbackModal('conexaoFeedback', 'Erro ao carregar dados', 'erro'); return; }
var ins = data.data.find(function(i) { return i.id === id; });
if (!ins) { mostrarFeedbackModal('conexaoFeedback', 'Instância não encontrada', 'erro'); return; }
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').classList.add('show');
};
// ===== EXCLUIR CONEXÃO =====
window.excluirConexao = async function(id, nome) {
mostrarConfirmacao('Excluir Conexao', 'A conexao WhatsApp "' + nome + '" sera removida.', nome, async function() {
var r = await api('/evolution/instances/' + id, { method: 'DELETE' });
if (r.success) carregarConexoes();
else mostrarFeedbackModal('conexaoFeedback', 'Erro: ' + r.error, 'erro');
});
};
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)
mostrarFeedbackModal('conexaoFeedback', 'Preencha todos os campos', 'erro'); return;
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 {
mostrarFeedbackModal('conexaoFeedback', 'Erro: ' + r.error, 'erro');
}
};
// ===== MODAIS =====
window.fecharModal = function(id) { document.getElementById(id).classList.remove('show'); };
function mostrarFeedbackModal(elId, msg, tipo) {
var fb = document.getElementById(elId);
if (!fb) return;
fb.textContent = msg;
fb.className = 'feedback-msg ' + tipo;
setTimeout(function() { fb.className = 'feedback-msg'; }, 4000);
}
// Fechar modal ao clicar fora
document.querySelectorAll('.modal-overlay').forEach(function(el) {
el.addEventListener('click', function(e) { if (e.target === this) this.classList.remove('show'); });
});
// ===== INICIAR =====
carregarEquipes();
carregarUsuarios();
carregarMenus();
carregarEtiquetas();
carregarConexoes();
carregarConfig();
carregarResolucao();
})();
</script>
<!-- Modal Confirmação de Exclusão -->
<div class="modal-overlay" id="modalConfirmar">
<div class="modal-box" style="max-width:420px">
<h3 id="confirmarTitulo">Confirmar exclusão</h3>
<p id="confirmarMensagem" style="font-size:14px;color:var(--text-secondary);margin-bottom:14px;line-height:1.5"></p>
<div class="form-group">
<label for="confirmarInput" style="font-size:13px;font-weight:600;color:var(--text-secondary);margin-bottom:5px">Digite o nome para confirmar:</label>
<input type="text" id="confirmarInput" placeholder="Digite exatamente o nome..." style="width:100%;padding:10px 14px;border:2px solid var(--border);border-radius:8px;font-size:14px;outline:none;background:var(--surface-2);color:var(--text-primary);transition:all .15s" onfocus="this.style.borderColor='var(--primary)';this.style.background='var(--surface)'" onblur="this.style.borderColor='var(--border)';this.style.background='var(--surface-2)'">
</div>
<div id="confirmarFeedback" class="feedback-msg"></div>
<div class="modal-footer">
<button class="btn-secondary" onclick="fecharModal('modalConfirmar')">Cancelar</button>
<button class="btn-danger" id="btnConfirmarExclusao" disabled>Excluir</button>
</div>
</div>
</div>
<script src="/js/dark-mode.js"></script>
<!-- impeccable-live-start -->
<script src="http://localhost:8400/live.js"></script>
<!-- impeccable-live-end -->
<!-- Bottom Tab Bar (mobile) -->
<script src="/js/tabbar.js"></script>
</body>
</html>