/**
* whatsapp.js β WhatsApp via Evolution API
*
* Connection flow: Create instance -> Scan QR -> Connected
* Left panel: conversation list (phone numbers + last message preview)
* Right panel: chat view with message bubbles
* Bottom: text input + send button
*/
(function () {
'use strict';
var token = localStorage.getItem('pos_token');
if (!token) { window.location.href = '/pos/login'; return; }
var API = '/pos/api/whatsapp';
var activePhone = null;
var pollTimer = null;
var statusPollTimer = null;
var connectionState = 'unknown'; // 'open', 'close', 'connecting', 'unknown'
// -- Helpers ---------------------------------------------------------------
function authHeaders() {
return { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' };
}
function api(method, path, body) {
var opts = { method: method, headers: authHeaders() };
if (body) opts.body = JSON.stringify(body);
return fetch(API + path, opts).then(function (r) {
if (r.status === 401) { window.location.href = '/pos/login'; }
return r.json();
});
}
function escHtml(s) {
var d = document.createElement('div');
d.textContent = s || '';
return d.innerHTML;
}
function fmtTime(iso) {
if (!iso) return '';
var d = new Date(iso);
var now = new Date();
var isToday = d.toDateString() === now.toDateString();
if (isToday) {
return d.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' });
}
return d.toLocaleDateString('es-MX', { day: '2-digit', month: 'short' }) +
' ' + d.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit' });
}
function fmtPhone(phone) {
if (!phone) return '';
if (phone.length === 13 && phone.startsWith('521')) {
return '+52 1 ' + phone.slice(3, 5) + ' ' + phone.slice(5, 9) + ' ' + phone.slice(9);
}
if (phone.length === 12 && phone.startsWith('52')) {
return '+52 ' + phone.slice(2, 4) + ' ' + phone.slice(4, 8) + ' ' + phone.slice(8);
}
return '+' + phone;
}
// -- DOM refs --------------------------------------------------------------
var convList = document.getElementById('convList');
var chatMessages = document.getElementById('chatMessages');
var chatHeader = document.getElementById('chatHeaderPhone');
var chatInput = document.getElementById('chatInput');
var sendBtn = document.getElementById('sendBtn');
var newChatBtn = document.getElementById('newChatBtn');
var emptyState = document.getElementById('emptyState');
var chatPanel = document.getElementById('chatPanel');
var statusDot = document.getElementById('statusDot');
var statusText = document.getElementById('statusText');
var connectSection = document.getElementById('connectSection');
var messengerArea = document.getElementById('messengerArea');
var qrImg = document.getElementById('qrImg');
var qrPlaceholder = document.getElementById('qrPlaceholder');
var connectBtn = document.getElementById('connectBtn');
var disconnectBtn = document.getElementById('disconnectBtn');
var refreshQrBtn = document.getElementById('refreshQrBtn');
// -- Connection management -------------------------------------------------
function checkInstanceStatus() {
api('GET', '/status').then(function (data) {
var state = (data.instance || data).state || data.state || 'close';
updateConnectionUI(state);
}).catch(function () {
updateConnectionUI('close');
});
}
function updateConnectionUI(state) {
connectionState = state;
if (state === 'open') {
statusDot.className = 'status-dot status-dot--ok';
statusText.textContent = 'Conectado';
connectSection.style.display = 'none';
messengerArea.style.display = 'flex';
disconnectBtn.style.display = '';
connectBtn.style.display = 'none';
// Load conversations + start polling on page load / reconnect
loadConversations();
startPolling();
} else if (state === 'connecting') {
statusDot.className = 'status-dot status-dot--warn';
statusText.textContent = 'Escaneando QR...';
connectSection.style.display = 'flex';
messengerArea.style.display = 'none';
disconnectBtn.style.display = 'none';
connectBtn.style.display = 'none';
refreshQrBtn.style.display = '';
} else {
// close or unknown
statusDot.className = 'status-dot status-dot--error';
statusText.textContent = 'Desconectado';
connectSection.style.display = 'flex';
messengerArea.style.display = 'none';
disconnectBtn.style.display = 'none';
connectBtn.style.display = '';
refreshQrBtn.style.display = 'none';
qrImg.style.display = 'none';
qrPlaceholder.style.display = '';
}
}
function doConnect() {
connectBtn.disabled = true;
connectBtn.textContent = 'Creando instancia...';
api('POST', '/connect').then(function (data) {
connectBtn.disabled = false;
connectBtn.textContent = 'Conectar WhatsApp';
if (data.error) {
alert('Error: ' + (data.error.message || data.error));
return;
}
// Instance created, now fetch QR
fetchQR();
}).catch(function () {
connectBtn.disabled = false;
connectBtn.textContent = 'Conectar WhatsApp';
alert('Error de red al crear instancia');
});
}
function fetchQR() {
qrPlaceholder.textContent = 'Generando QR...';
api('GET', '/qr').then(function (data) {
var base64 = data.qr || data.base64 || data.qrcode || '';
if (base64) {
qrImg.src = base64.startsWith('data:') ? base64 : 'data:image/png;base64,' + base64;
qrImg.style.display = 'block';
qrPlaceholder.style.display = 'none';
refreshQrBtn.style.display = '';
updateConnectionUI('connecting');
// Start polling for connection state while QR is shown
startStatusPolling();
} else if ((data.instance && data.instance.state === 'open') || data.state === 'open') {
// Already connected
updateConnectionUI('open');
loadConversations();
} else {
qrPlaceholder.textContent = 'No se pudo generar el QR. Intenta de nuevo.';
qrPlaceholder.style.display = '';
qrImg.style.display = 'none';
}
}).catch(function () {
qrPlaceholder.textContent = 'Error al obtener QR';
});
}
function doDisconnect() {
if (!confirm('Desconectar WhatsApp?')) return;
api('POST', '/logout').then(function () {
updateConnectionUI('close');
stopStatusPolling();
});
}
function startStatusPolling() {
stopStatusPolling();
statusPollTimer = setInterval(function () {
api('GET', '/status').then(function (data) {
var state = (data.instance || data).state || data.state || 'close';
if (state === 'open') {
updateConnectionUI('open');
stopStatusPolling();
loadConversations();
startPolling();
}
});
}, 3000);
}
function stopStatusPolling() {
if (statusPollTimer) {
clearInterval(statusPollTimer);
statusPollTimer = null;
}
}
connectBtn.addEventListener('click', doConnect);
disconnectBtn.addEventListener('click', doDisconnect);
refreshQrBtn.addEventListener('click', fetchQR);
// -- Load conversations ----------------------------------------------------
function loadConversations() {
api('GET', '/conversations').then(function (data) {
var convs = data.conversations || [];
if (convs.length === 0) {
convList.innerHTML = '
No hay conversaciones
';
return;
}
var html = '';
convs.forEach(function (c) {
var isActive = c.phone === activePhone;
var dirIcon = c.last_direction === 'outgoing' ? 'β ' : 'β ';
// Show contact name if available, otherwise try to format the phone.
// LID numbers (15+ digits, no country code pattern) show as "Contacto"
var displayName = c.contact_name || '';
if (!displayName) {
var isLid = c.phone.length > 13 || !/^(52|1|44|34)/.test(c.phone);
displayName = isLid ? 'Contacto WhatsApp' : fmtPhone(c.phone);
}
html += '