adição de agendamento de conversa
This commit is contained in:
+162
-2
@@ -825,9 +825,31 @@ body.dark-mode .msg.enviando { opacity: 0.4; }
|
||||
|
||||
<!-- Modal confirmar finalização -->
|
||||
<div class="modal-overlay" id="modalConfirmarFinalizar">
|
||||
<div class="modal">
|
||||
<div class="modal" style="max-width:420px">
|
||||
<h3>⚠️ Finalizar conversa</h3>
|
||||
<p style="font-size:13px;color:#6b7280;margin-bottom:16px">Tem certeza que deseja finalizar este atendimento? A conversa será movida para finalizadas.</p>
|
||||
<p style="font-size:13px;color:#6b7280;margin-bottom:12px">Tem certeza que deseja finalizar este atendimento?</p>
|
||||
|
||||
<!-- Agendamento -->
|
||||
<div id="agendamentoBox" style="margin-bottom:14px;padding:14px;background:var(--surface-2);border-radius:8px;border:1px solid var(--border)">
|
||||
<label style="display:flex;align-items:center;gap:8px;font-size:13px;font-weight:600;color:var(--text-secondary);cursor:pointer;margin-bottom:0">
|
||||
<input type="checkbox" id="chkAgendar" onchange="toggleAgendamento()" style="accent-color:var(--primary);width:16px;height:16px"> 📅 Agendar retorno
|
||||
</label>
|
||||
<div id="agendamentoCampos" style="display:none;margin-top:12px">
|
||||
<div style="display:flex;gap:8px;margin-bottom:8px">
|
||||
<div style="flex:1">
|
||||
<label style="font-size:11px;font-weight:600;color:var(--text-muted);display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.04em">Data</label>
|
||||
<input type="date" id="agData" style="width:100%;padding:8px 10px;border:2px solid var(--border);border-radius:8px;font-size:13px;outline:none;background:var(--surface);color:var(--text-primary)">
|
||||
</div>
|
||||
<div style="width:90px">
|
||||
<label style="font-size:11px;font-weight:600;color:var(--text-muted);display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.04em">Hora</label>
|
||||
<input type="time" id="agHora" style="width:100%;padding:8px 10px;border:2px solid var(--border);border-radius:8px;font-size:13px;outline:none;background:var(--surface);color:var(--text-primary)">
|
||||
</div>
|
||||
</div>
|
||||
<label style="font-size:11px;font-weight:600;color:var(--text-muted);display:block;margin-bottom:4px;text-transform:uppercase;letter-spacing:0.04em">Anotação</label>
|
||||
<textarea id="agNota" rows="2" placeholder="Ex: Confirmar pagamento do boleto..." style="width:100%;padding:8px 10px;border:2px solid var(--border);border-radius:8px;font-size:13px;font-family:inherit;resize:vertical;outline:none;background:var(--surface);color:var(--text-primary)"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<button onclick="fecharModal('modalConfirmarFinalizar')">Cancelar</button>
|
||||
<button class="btn-primary" onclick="fecharModal('modalConfirmarFinalizar');finalizarConversa()">Finalizar</button>
|
||||
@@ -835,6 +857,23 @@ body.dark-mode .msg.enviando { opacity: 0.4; }
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal retorno agendado -->
|
||||
<div class="modal-overlay" id="modalRetorno">
|
||||
<div class="modal" style="max-width:460px">
|
||||
<h3>📅 Retorno agendado</h3>
|
||||
<p style="font-size:13px;color:var(--text-muted);margin-bottom:14px">Conversa criada. Revise o texto antes de enviar:</p>
|
||||
<input type="hidden" id="retornoConversaId">
|
||||
<div class="form-group">
|
||||
<textarea id="retornoTextoSugerido" rows="4" style="width:100%;padding:10px 14px;border:2px solid var(--border);border-radius:8px;font-size:14px;font-family:inherit;resize:vertical;outline:none;background:var(--surface-2);color:var(--text-primary)"></textarea>
|
||||
</div>
|
||||
<p style="font-size:11px;color:var(--text-faint);margin-bottom:16px">Use [CLIENTE], [CONTATO], [EMPRESAF] para dados automáticos.</p>
|
||||
<div class="btn-group">
|
||||
<button onclick="document.getElementById('retornoConversaId').value='';fecharModal('modalRetorno')">Cancelar</button>
|
||||
<button class="btn-primary" onclick="confirmarRetorno()">✅ Abrir conversa</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal associar cliente -->
|
||||
<div class="modal-overlay" id="modalAssociar">
|
||||
<div class="modal">
|
||||
@@ -881,6 +920,7 @@ body.dark-mode .msg.enviando { opacity: 0.4; }
|
||||
<a href="#" onclick="mudarFiltro('unassigned',this)">Sem atend. (0)</a>
|
||||
<a href="#" id="tabTodasConvs" onclick="mudarFiltro('all',this)" class="admin-only-nav">Todas</a>
|
||||
<a href="#" onclick="mudarFiltro('equipe',this)" id="tabEquipeConvs">Equipe (0)</a>
|
||||
<a href="#" onclick="mudarFiltro('scheduled',this)" id="tabAgendadas">📅 Agendadas (0)</a>
|
||||
</div>
|
||||
<div class="search-box">
|
||||
<input type="text" id="searchConv" placeholder="🔍 Buscar conversas..." onkeyup="carregarConversas()">
|
||||
@@ -1080,6 +1120,11 @@ document.querySelectorAll('.sidebar-left .nav-tabs a').forEach(function(a) {
|
||||
|
||||
// ===== CARREGAR CONVERSAS =====
|
||||
async function carregarConversas() {
|
||||
// Aba Agendadas: carrega da API de agendamentos
|
||||
if (filtroAtual === 'scheduled') {
|
||||
return carregarAgendadas();
|
||||
}
|
||||
|
||||
const busca = document.getElementById('searchConv').value;
|
||||
var statusFilter = document.getElementById('chkFinalizadas').checked ? 'A,E,F' : 'A,E';
|
||||
try {
|
||||
@@ -1818,6 +1863,22 @@ dropArea.addEventListener('drop', function(e) {
|
||||
});
|
||||
|
||||
// ===== FINALIZAR =====
|
||||
window.toggleAgendamento = function() {
|
||||
var show = document.getElementById('chkAgendar').checked;
|
||||
document.getElementById('agendamentoCampos').style.display = show ? 'block' : 'none';
|
||||
if (show) {
|
||||
if (!document.getElementById('agData').value) {
|
||||
document.getElementById('agData').value = new Date().toISOString().split('T')[0];
|
||||
}
|
||||
// Preenche anotação com o texto da resolução (se existir)
|
||||
var txtResolucao = document.getElementById('campoResolucao');
|
||||
var nota = document.getElementById('agNota');
|
||||
if (txtResolucao && nota && !nota.value) {
|
||||
nota.value = (txtResolucao.value || '').trim();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.finalizarConversa = async function() {
|
||||
if (!conversaAtiva) return;
|
||||
|
||||
@@ -1847,6 +1908,19 @@ window.finalizarConversa = async function() {
|
||||
});
|
||||
var data = await res.json();
|
||||
if (data.success) {
|
||||
// Agenda retorno se checkbox marcado
|
||||
if (document.getElementById('chkAgendar') && document.getElementById('chkAgendar').checked) {
|
||||
var agData = document.getElementById('agData').value;
|
||||
var agHora = document.getElementById('agHora').value || null;
|
||||
var agNota = document.getElementById('agNota').value.trim() || null;
|
||||
if (agData) {
|
||||
fetch('/api/' + alias + '/conversations/' + conversaAtiva + '/schedule', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ data: agData, hora: agHora, nota: agNota })
|
||||
}).catch(function(){});
|
||||
}
|
||||
}
|
||||
document.querySelector('.btn-finalizar').textContent = '✅ Finalizado';
|
||||
document.querySelector('.btn-finalizar').disabled = true;
|
||||
document.querySelector('.btn-finalizar').style.opacity = '0.6';
|
||||
@@ -2116,9 +2190,95 @@ window.mudarFiltro = function(filtro, el) {
|
||||
document.querySelectorAll('.nav-tabs a').forEach(function(a) { a.classList.remove('active'); });
|
||||
el.classList.add('active');
|
||||
filtroAtual = filtro;
|
||||
// Mostra/esconde busca e checkbox de finalizadas (não fazem sentido para agendadas)
|
||||
var searchBox = document.querySelector('.search-box');
|
||||
if (filtro === 'scheduled') {
|
||||
if (searchBox) searchBox.style.display = 'none';
|
||||
} else {
|
||||
if (searchBox) searchBox.style.display = '';
|
||||
}
|
||||
carregarConversas();
|
||||
};
|
||||
|
||||
// ===== AGENDADAS =====
|
||||
async function carregarAgendadas() {
|
||||
var dataSel = document.getElementById('agendadasData');
|
||||
var data = dataSel ? dataSel.value : new Date().toISOString().split('T')[0];
|
||||
var list = document.getElementById('conversationsList');
|
||||
list.innerHTML = '<div style="padding:24px;text-align:center;color:rgba(255,255,255,0.4);font-size:13px">Carregando agendamentos...</div>';
|
||||
|
||||
try {
|
||||
var res = await fetch('/api/' + alias + '/schedules?empresaId=' + empresaId + '&data=' + data, {
|
||||
headers: { 'Authorization': 'Bearer ' + token }
|
||||
});
|
||||
var d = await res.json();
|
||||
if (!d.success) return;
|
||||
|
||||
var dataFmt = data.split('-').reverse().join('/');
|
||||
var html = '<div style="padding:10px 12px;border-bottom:1px solid rgba(255,255,255,0.06);display:flex;align-items:center;gap:8px">' +
|
||||
'<span style="font-size:13px;color:rgba(255,255,255,0.7)">📅</span>' +
|
||||
'<input type="date" id="agendadasData" value="' + data + '" onchange="carregarAgendadas()" style="flex:1;padding:6px 8px;border:1px solid rgba(255,255,255,0.15);border-radius:6px;font-size:12px;background:rgba(255,255,255,0.06);color:#fff;outline:none">' +
|
||||
'<span style="font-size:12px;color:rgba(255,255,255,0.5)">' + (d.count || 0) + ' agend.</span></div>';
|
||||
|
||||
if (!d.data || d.data.length === 0) {
|
||||
html += '<div style="padding:24px;text-align:center;color:rgba(255,255,255,0.35);font-size:13px">Nenhum agendamento para ' + dataFmt + '</div>';
|
||||
} else {
|
||||
d.data.forEach(function(a) {
|
||||
var nome = a.nomeContato || a.numero || '-';
|
||||
var cor = avatarColor(nome);
|
||||
var ini = avatarInitials(nome);
|
||||
var hora = a.hora ? a.hora.substring(0,5) : '';
|
||||
html += '<div class="conv-item" style="cursor:pointer" onclick="reabrirAgendamento(' + a.id + ',' + a.conversaId + ')">' +
|
||||
'<div class="conv-avatar" style="background:' + cor + '">' + ini + '</div>' +
|
||||
'<div class="conv-body">' +
|
||||
'<div class="conv-nome">' + nome + (hora ? ' <span style="font-size:10px;opacity:0.6">🕐 ' + hora + '</span>' : '') + '</div>' +
|
||||
'<div class="conv-ultima" style="font-size:11px">' + (a.nota || 'Sem anotação') + '</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
});
|
||||
}
|
||||
list.innerHTML = html;
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
window.reabrirAgendamento = async function(agId, convId) {
|
||||
try {
|
||||
var res = await fetch('/api/' + alias + '/schedules/' + agId + '/reopen', {
|
||||
method: 'POST', headers: { 'Authorization': 'Bearer ' + token }
|
||||
});
|
||||
var d = await res.json();
|
||||
if (d.success) {
|
||||
// Mostra modal com o texto sugerido
|
||||
document.getElementById('retornoTextoSugerido').value = d.textoSugerido || '';
|
||||
document.getElementById('retornoConversaId').value = d.conversaId;
|
||||
document.getElementById('modalRetorno').classList.add('show');
|
||||
}
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
window.confirmarRetorno = function() {
|
||||
var convId = document.getElementById('retornoConversaId').value;
|
||||
var texto = document.getElementById('retornoTextoSugerido').value.trim();
|
||||
fecharModal('modalRetorno');
|
||||
if (!convId) return;
|
||||
// Abre a conversa e preenche o input
|
||||
mudarFiltro('mine', document.querySelector('.nav-tabs a'));
|
||||
carregarConversas();
|
||||
setTimeout(function() {
|
||||
abrirConversa(parseInt(convId));
|
||||
// Aguarda o chat carregar e preenche o input
|
||||
setTimeout(function() {
|
||||
var input = document.getElementById('msgInput');
|
||||
if (input && texto) {
|
||||
input.value = texto;
|
||||
input.style.height = 'auto';
|
||||
input.style.height = input.scrollHeight + 'px';
|
||||
input.focus();
|
||||
}
|
||||
}, 500);
|
||||
}, 300);
|
||||
};
|
||||
|
||||
// Mostra
|
||||
|
||||
// ===== SELETOR DE EMPRESA (usuário com acesso a mais de uma) =====
|
||||
|
||||
Reference in New Issue
Block a user