98 lines
6.0 KiB
JavaScript
98 lines
6.0 KiB
JavaScript
(function(){
|
||
const cfg = window.AI_CHATBOT_CONFIG || {};
|
||
if (!cfg.enabled || !cfg.floatingEnabled) return;
|
||
const csrf = document.body?.dataset?.csrfToken || cfg.csrfToken || '';
|
||
let sessionId = localStorage.getItem('ai_chatbot_session_id') || (crypto.randomUUID ? crypto.randomUUID() : String(Date.now()));
|
||
localStorage.setItem('ai_chatbot_session_id', sessionId);
|
||
let selectedSlot = null;
|
||
let selectedProfessional = null;
|
||
const launcher = document.createElement('button');
|
||
launcher.className = 'ai-chatbot-launcher';
|
||
launcher.type = 'button';
|
||
launcher.title = cfg.assistantName || 'Chatbot IA';
|
||
launcher.innerHTML = '<i class="bi bi-robot"></i>';
|
||
const panel = document.createElement('div');
|
||
panel.className = 'ai-chatbot-panel';
|
||
panel.innerHTML = `
|
||
<div class="ai-chatbot-header">
|
||
<div><div class="ai-chatbot-title">${escapeHtml(cfg.assistantName || 'Asistente Virtual')}</div><div class="ai-chatbot-subtitle">Especialidades · precios · turnos</div></div>
|
||
<button type="button" class="ai-chatbot-close" aria-label="Cerrar">×</button>
|
||
</div>
|
||
<div class="ai-chatbot-body" id="aiChatbotBody"></div>
|
||
<div class="ai-chatbot-footer"><form class="ai-chatbot-form" id="aiChatbotForm"><input class="ai-chatbot-input" id="aiChatbotInput" autocomplete="off" placeholder="Escribí tu consulta..."><button class="ai-chatbot-send" type="submit"><i class="bi bi-send"></i></button></form></div>`;
|
||
document.body.appendChild(panel);
|
||
document.body.appendChild(launcher);
|
||
const body = panel.querySelector('#aiChatbotBody');
|
||
const form = panel.querySelector('#aiChatbotForm');
|
||
const input = panel.querySelector('#aiChatbotInput');
|
||
function escapeHtml(s){ return String(s || '').replace(/[&<>'"]/g, c => ({'&':'&','<':'<','>':'>',"'":''','"':'"'}[c])); }
|
||
function addMessage(text, who='bot'){
|
||
const wrap = document.createElement('div'); wrap.className = `ai-chatbot-message ${who}`;
|
||
wrap.innerHTML = `<div class="ai-chatbot-bubble">${escapeHtml(text)}</div>`;
|
||
body.appendChild(wrap); body.scrollTop = body.scrollHeight;
|
||
}
|
||
function addActions(actions){
|
||
if(!actions || !actions.length) return;
|
||
const wrap = document.createElement('div'); wrap.className = 'ai-chatbot-actions';
|
||
actions.forEach(a => {
|
||
const b = document.createElement('button'); b.type='button'; b.className='ai-chatbot-action'; b.textContent=a.label || 'Abrir';
|
||
b.addEventListener('click', () => { if(a.type === 'open_booking_form') showBookingForm(); else if(a.url) { if(a.url.startsWith('#')) location.hash=a.url; else location.href=a.url; } });
|
||
wrap.appendChild(b);
|
||
});
|
||
body.appendChild(wrap); body.scrollTop = body.scrollHeight;
|
||
}
|
||
launcher.addEventListener('click', () => { panel.classList.toggle('open'); if(panel.classList.contains('open') && !body.dataset.started){ body.dataset.started='1'; addMessage(cfg.welcomeMessage || 'Hola, soy el asistente virtual.'); addActions([{type:'open_booking_form', label:'Solicitar turno'}, {type:'link', label:'Ver precios', url:'#services'}, {type:'link', label:'Contacto', url:'#contact'}]); input.focus(); } });
|
||
panel.querySelector('.ai-chatbot-close').addEventListener('click', () => panel.classList.remove('open'));
|
||
form.addEventListener('submit', async (e) => { e.preventDefault(); const msg = input.value.trim(); if(!msg) return; input.value=''; addMessage(msg, 'user'); await sendMessage(msg); });
|
||
async function sendMessage(message){
|
||
addMessage('Estoy consultando la información disponible...', 'bot');
|
||
const last = body.lastElementChild;
|
||
try{
|
||
const resp = await fetch(cfg.messageUrl, {method:'POST', headers:{'Content-Type':'application/json','X-CSRF-Token':csrf}, body:JSON.stringify({message, session_id: sessionId})});
|
||
const data = await resp.json();
|
||
if(last) last.remove();
|
||
if(!data.ok) { addMessage(data.error || 'No pude procesar la consulta.'); return; }
|
||
sessionId = data.session_id || sessionId; localStorage.setItem('ai_chatbot_session_id', sessionId);
|
||
addMessage(data.reply || 'Sin respuesta.'); addActions(data.actions || []);
|
||
}catch(err){ if(last) last.remove(); addMessage('No pude conectarme con el asistente. Probá nuevamente.'); }
|
||
}
|
||
async function showBookingForm(){
|
||
const card = document.createElement('div');
|
||
card.className='ai-booking-card';
|
||
card.innerHTML = `
|
||
<div class="booking-wizard-card">
|
||
<div class="booking-wizard-header">
|
||
<span class="badge rounded-pill text-bg-light border" id="bwStepBadge">Turno</span>
|
||
<h3 class="h6 mb-1" id="bwTitle">Solicitud guiada de turno</h3>
|
||
<p class="text-muted small mb-0" id="bwSubtitle">Te voy a guiar paso a paso.</p>
|
||
</div>
|
||
<div id="bwContent" class="booking-wizard-content"></div>
|
||
<div class="booking-wizard-actions">
|
||
<button type="button" class="btn btn-sm btn-outline-secondary" id="bwPrev">Anterior</button>
|
||
<button type="button" class="btn btn-sm btn-primary" id="bwNext">Siguiente</button>
|
||
</div>
|
||
</div>`;
|
||
body.appendChild(card); body.scrollTop = body.scrollHeight;
|
||
if(window.BookingWizard && window.BookingWizard.create){
|
||
window.BookingWizard.create(card, {
|
||
csrfToken: csrf,
|
||
optionsUrl: cfg.bookingWizardOptionsUrl || cfg.bookingOptionsUrl,
|
||
slotsUrl: cfg.bookingWizardSlotsUrl || cfg.bookingSlotsUrl,
|
||
createUrl: cfg.bookingWizardCreateUrl || cfg.bookingCreateUrl,
|
||
linkedPatient: null,
|
||
}, {
|
||
source: 'ai_chatbot',
|
||
defaultMode: 'unknown',
|
||
onSuccess: (data) => {
|
||
addMessage(`Turno confirmado con ${data.professional}, el ${data.date} a las ${data.time}.`);
|
||
if(data.branch) addMessage(`Sede seleccionada: ${data.branch}.`);
|
||
addActions([{type:'link', label:'Ver comprobante', url:data.success_url}]);
|
||
}
|
||
});
|
||
} else {
|
||
addMessage('No pude cargar el asistente de turnos. Podés usar el botón Reservar turno del sitio.');
|
||
addActions([{type:'link', label:'Abrir turnos', url:'/booking'}]);
|
||
}
|
||
}
|
||
})();
|