From b8b4bffde2691f0a4110b2cd0c5b19ec6a16d9a4 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Fri, 13 Mar 2026 08:57:10 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20sesiones=20minimizadas=20m=C3=BAltiples?= =?UTF-8?q?=20=E2=80=94=20p=C3=ADldoras=20con=20nombre=20de=20chat=20y=20n?= =?UTF-8?q?avegaci=C3=B3n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conversations.php | 331 ++++++++++++++++++++++++++++++---------------- 1 file changed, 220 insertions(+), 111 deletions(-) diff --git a/conversations.php b/conversations.php index 36199bb..a232ef4 100644 --- a/conversations.php +++ b/conversations.php @@ -6432,42 +6432,60 @@ if (!isset($_SESSION['user_id'])) { #modalAgendarDomicilio .is-invalid ~ .field-err, #modalAgendarDomicilio .is-invalid + .field-err { display:block; } -/* ── Modo flotante (minimizado) ── */ -#modalAgendarDomicilio.dom-flotante { - position: fixed !important; - top: auto !important; - left: auto !important; - bottom: 16px !important; - right: 16px !important; - width: auto !important; - margin: 0 !important; - z-index: 1055; - display: block !important; - pointer-events: auto; +/* ── Barra de sesiones minimizadas ── */ +#labdom-pills-bar { + position: fixed; + bottom: 0; + left: 0; + z-index: 1060; + display: flex; + flex-wrap: wrap; + gap: 6px; + padding: 8px 14px 8px; + pointer-events: none; + max-width: 80vw; } -#modalAgendarDomicilio.dom-flotante .modal-dialog { - margin: 0; - width: 320px; - max-height: none; - transform: none !important; -} -#modalAgendarDomicilio.dom-flotante .modal-body, -#modalAgendarDomicilio.dom-flotante #labdom-form-panel, -#modalAgendarDomicilio.dom-flotante #labdom-footer, -#modalAgendarDomicilio.dom-flotante #labdom-ok-panel { display: none !important; } -#modalAgendarDomicilio.dom-flotante .modal-content { - box-shadow: 0 8px 32px rgba(0,0,0,.28); - border-radius: 12px; -} -#modalAgendarDomicilio.dom-flotante .modal-header { - cursor: move; - border-radius: 12px; +.labdom-pill { + pointer-events: all; + display: flex; + align-items: center; + gap: 5px; + background: linear-gradient(135deg, #1565c0, #0d47a1); + color: #fff; + border-radius: 24px; + padding: 5px 8px 5px 6px; + cursor: pointer; + font-size: .78rem; + box-shadow: 0 4px 16px rgba(0,0,0,.28); + max-width: 280px; + transition: background .15s, transform .1s, box-shadow .1s; user-select: none; + animation: pillIn .18s ease; } -/* Botón restaurar dentro del header minimizado */ -.labdom-restore-hint { - font-size:.72rem; opacity:.8; white-space:nowrap; +.labdom-pill:hover { background: linear-gradient(135deg,#1976d2,#1565c0); transform: translateY(-2px); box-shadow: 0 6px 22px rgba(0,0,0,.32); } +@keyframes pillIn { from { transform:translateY(10px); opacity:0 } to { transform:translateY(0); opacity:1 } } +.labdom-pill-num { + background: rgba(255,255,255,.28); + border-radius: 50%; + width: 22px; height: 22px; + display: flex; align-items: center; justify-content: center; + font-weight: 700; font-size: .72rem; flex-shrink: 0; } +.labdom-pill-body { display:flex; flex-direction:column; overflow:hidden; } +.labdom-pill-chat { font-weight: 600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width: 110px; font-size:.78rem; } +.labdom-pill-pac { font-size:.68rem; opacity:.85; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width: 110px; } +.labdom-pill-close { + background: rgba(255,255,255,.18); + border: none; color: #fff; + border-radius: 50%; + width: 17px; height: 17px; + display: flex; align-items:center; justify-content:center; + font-size: .6rem; cursor: pointer; padding: 0; flex-shrink: 0; + transition: background .12s; + line-height: 1; +} +.labdom-pill-close:hover { background: rgba(255,50,50,.55); } + /* Botón minimizar */ #labdom-btn-minimizar { background: rgba(255,255,255,.18); @@ -6493,7 +6511,7 @@ if (!isset($_SESSION['user_id'])) {
- @@ -6773,6 +6791,10 @@ const labDomicilio = (() => { let _histDirs = []; // direcciones únicas previas del paciente let _bsModal = null; + // Sesiones minimizadas (múltiples chats en paralelo) + let _sesiones = []; // [{ id, chatUserId, chatName, chatPhone, convId, pacienteId, pacNombre, histDirs, msgId, localFile, cardVisible, pacInfo, fields }] + let _sesionIdCounter = 0; + // ── helpers DOM ────────────────────────────────────────────────────────── const $ = id => document.getElementById(id); const fv = id => $( id).value.trim(); @@ -7281,93 +7303,177 @@ const labDomicilio = (() => { return el ? el.textContent.trim() : ''; }, - /* ─── Minimizar / restaurar modal ─── - * Al minimizar: oculta backdrop, convierte el modal en flotante - * sin perder ningún dato del formulario. - * Al restaurar: vuelve al estado normal de Bootstrap. */ + /* ───────────────────────────────────────────────────────────────── + * SISTEMA DE SESIONES MINIMIZADAS + * Al minimizar: guarda estado del form en memoria y muestra una + * píldora en la barra inferior con el nombre del chat + paciente. + * Al hacer clic en la píldora: navega al chat y restaura el form. + * ───────────────────────────────────────────────────────────────── */ + minimizar() { - const modalEl = document.getElementById('modalAgendarDomicilio'); - const isMin = !modalEl.classList.contains('dom-flotante'); + // Capturar nombre e identificadores del chat activo + const chatName = document.getElementById('chat-name-text')?.textContent?.trim() + || document.getElementById('chat-name')?.textContent?.trim() + || 'Chat'; + const chatPhone = document.getElementById('chat-phone')?.textContent?.trim() || ''; + const chatUserId = (typeof chatApp !== 'undefined') ? (chatApp.currentUserId || 0) : 0; - if (isMin) { - /* — Pasar a modo flotante — */ - // 1. Quitar backdrop - document.querySelectorAll('.modal-backdrop').forEach(b => b.remove()); - document.body.classList.remove('modal-open'); - document.body.style.overflow = ''; - document.body.style.paddingRight = ''; + // Construir objeto de sesión + const sesion = { + id: ++_sesionIdCounter, + chatUserId, + chatName, + chatPhone, + convId: _convId, + pacienteId: _pacienteId, + pacNombre: _pacNombre, + histDirs: [..._histDirs], + msgId: _msgId, + localFile: _localFile, + cardVisible: !document.getElementById('labdom-pac-card')?.classList.contains('d-none'), + pacInfo: { + nombre: document.getElementById('labdom-pac-nombre')?.textContent || '', + info: document.getElementById('labdom-pac-info')?.textContent || '', + tel: document.getElementById('labdom-pac-tel')?.textContent || '', + email: document.getElementById('labdom-pac-email')?.textContent || '', + eps: document.getElementById('labdom-pac-eps')?.textContent || '', + }, + fields: {}, + }; - // 2. Desvincular de Bootstrap para posicionarlo libre - modalEl.removeAttribute('aria-modal'); - modalEl.removeAttribute('role'); - // Mantener visible pero fuera del flujo Bootstrap - modalEl.style.display = 'block'; - modalEl.classList.add('dom-flotante'); + // Capturar todos los campos del formulario + ['labdom-fecha','labdom-hora','labdom-tipo','labdom-direccion','labdom-barrio', + 'labdom-ciudad','labdom-indicaciones','labdom-examenes','labdom-notas', + 'labdom-seguro-nombre','labdom-autorizacion','labdom-copago', + 'labdom-valor-domicilio','labdom-valor-copago','labdom-pac-fnac', + 'labdom-pac-correo','labdom-pac-busq'].forEach(id => { + const el = document.getElementById(id); + if (el) sesion.fields[id] = el.value; + }); + const tcr = document.querySelector('input[name="labdom-tipo-cliente"]:checked'); + sesion.fields['labdom-tipo-cliente'] = tcr ? tcr.value : 'particular'; - // 3. Título del botón - const icon = document.querySelector('#labdom-btn-minimizar i'); - if (icon) { icon.className = 'fas fa-window-restore'; } - const btn = document.getElementById('labdom-btn-minimizar'); - if (btn) btn.title = 'Restaurar modal'; + _sesiones.push(sesion); - // 4. Activar drag - labDomicilio._initDrag(); + // Cerrar el modal completamente + if (_bsModal) _bsModal.hide(); - } else { - /* — Restaurar modo normal — */ - modalEl.classList.remove('dom-flotante'); - // Limpiar posición manual que puso _initDrag - const dialog = modalEl.querySelector('.modal-dialog'); - if (dialog) { - dialog.style.position = ''; - dialog.style.left = ''; - dialog.style.top = ''; - dialog.style.margin = ''; - } - - // Re-mostrar con Bootstrap para restaurar backdrop - if (_bsModal) { - // Bootstrap detecta el estado por aria-modal - modalEl.setAttribute('aria-modal', 'true'); - modalEl.setAttribute('role', 'dialog'); - _bsModal.show(); - } - - const icon = document.querySelector('#labdom-btn-minimizar i'); - if (icon) { icon.className = 'fas fa-window-minimize'; } - const btn = document.getElementById('labdom-btn-minimizar'); - if (btn) btn.title = 'Minimizar — sigue agendando mientras copias datos'; - } + // Renderizar píldoras + labDomicilio._renderPills(); }, - /* ─── Drag de la ventana flotante ─── */ - _initDrag() { - const dialog = document.querySelector('#modalAgendarDomicilio .modal-dialog'); - const handle = document.querySelector('#modalAgendarDomicilio .modal-header'); - if (!handle || handle._dragInit) return; - handle._dragInit = true; - let sx, sy, sl, st; - handle.addEventListener('mousedown', ev => { - if (ev.target.closest('button')) return; - const r = dialog.getBoundingClientRect(); - sx = ev.clientX; sy = ev.clientY; - sl = r.left; st = r.top; - dialog.style.position = 'fixed'; - dialog.style.margin = '0'; - dialog.style.left = sl + 'px'; - dialog.style.top = st + 'px'; - ev.preventDefault(); - const move = e => { - dialog.style.left = (sl + e.clientX - sx) + 'px'; - dialog.style.top = (st + e.clientY - sy) + 'px'; - }; - const up = () => { - document.removeEventListener('mousemove', move); - document.removeEventListener('mouseup', up); - }; - document.addEventListener('mousemove', move); - document.addEventListener('mouseup', up); + /* Renderiza la barra de píldoras de sesiones minimizadas */ + _renderPills() { + let bar = document.getElementById('labdom-pills-bar'); + if (!bar) { + bar = document.createElement('div'); + bar.id = 'labdom-pills-bar'; + document.body.appendChild(bar); + } + bar.innerHTML = _sesiones.map((s, i) => { + const num = i + 1; + return ` +
+ ${num} + + ${escLab(s.chatName)} + ${s.pacNombre ? `${escLab(s.pacNombre)}` : `Sin paciente aún`} + + +
`; + }).join(''); + }, + + /* Restaura una sesión minimizada (y navega al chat si hace falta) */ + async _restaurarSesion(sesId) { + const sesion = _sesiones.find(s => s.id === sesId); + if (!sesion) return; + + // Quitar de la lista ANTES de abrir el modal + _sesiones = _sesiones.filter(s => s.id !== sesId); + labDomicilio._renderPills(); + + // Navegar al chat correspondiente si no es el activo + const chatUserId = (typeof chatApp !== 'undefined') ? (chatApp.currentUserId || 0) : 0; + if (sesion.chatUserId && sesion.chatUserId != chatUserId && typeof chatApp !== 'undefined') { + const conv = chatApp.conversations?.find(c => c.user_id == sesion.chatUserId); + if (conv) { + await chatApp.openConversation(conv.user_id, conv.name || sesion.chatName, conv.phone_number || sesion.chatPhone); + await new Promise(r => setTimeout(r, 280)); + } + } + + // Restaurar estado interno + _convId = sesion.convId; + _pacienteId = sesion.pacienteId; + _pacNombre = sesion.pacNombre; + _histDirs = sesion.histDirs || []; + _msgId = sesion.msgId; + _localFile = sesion.localFile; + + // Mostrar formulario + mostrarFormulario(); + labDomicilio._resetFull(); // limpia el DOM + + // Restaurar campos del formulario + const f = sesion.fields || {}; + ['labdom-fecha','labdom-hora','labdom-tipo','labdom-direccion','labdom-barrio', + 'labdom-ciudad','labdom-indicaciones','labdom-examenes','labdom-notas', + 'labdom-seguro-nombre','labdom-autorizacion','labdom-copago', + 'labdom-valor-domicilio','labdom-valor-copago','labdom-pac-fnac', + 'labdom-pac-correo','labdom-pac-busq'].forEach(id => { + const el = document.getElementById(id); + if (el && f[id] !== undefined) el.value = f[id]; }); + const tcVal = f['labdom-tipo-cliente'] || 'particular'; + const tcEl = document.querySelector(`input[name="labdom-tipo-cliente"][value="${tcVal}"]`); + if (tcEl) { tcEl.checked = true; labDomicilio._toggleSeguro(); } + + // Restaurar tarjeta de paciente si había uno seleccionado + if (sesion.cardVisible && sesion.pacienteId) { + const $ = id => document.getElementById(id); + $('labdom-pac-buscar')?.classList.add('d-none'); + $('labdom-pac-card')?.classList.remove('d-none'); + $('labdom-btn-cambiar-pac')?.classList.remove('d-none'); + if ($('labdom-pac-nombre')) $('labdom-pac-nombre').textContent = sesion.pacInfo.nombre; + if ($('labdom-pac-info')) $('labdom-pac-info').textContent = sesion.pacInfo.info; + if ($('labdom-header-pac')) $('labdom-header-pac').textContent = sesion.pacNombre; + if ($('labdom-pac-tel')) $('labdom-pac-tel').textContent = sesion.pacInfo.tel; + if ($('labdom-pac-email')) $('labdom-pac-email').textContent = sesion.pacInfo.email; + if (sesion.pacInfo.eps && $('labdom-pac-eps')) { + $('labdom-pac-eps').textContent = sesion.pacInfo.eps; + if ($('labdom-pac-eps-row')) $('labdom-pac-eps-row').style.display = ''; + } + if (sesion.pacInfo.tel || sesion.pacInfo.email) { + $('labdom-pac-contacto')?.classList.remove('d-none'); + } + if (sesion.histDirs?.length) { + $('labdom-hist-cont')?.classList.remove('d-none'); + if ($('labdom-hist-chips')) { + $('labdom-hist-chips').innerHTML = sesion.histDirs.map((d, i) => + `` + ).join(''); + } + } + } + + labDomicilio._calcTotal(); + + // Abrir el modal + if (!_bsModal) _bsModal = new bootstrap.Modal('#modalAgendarDomicilio', { backdrop: true, keyboard: true }); + _bsModal.show(); + }, + + /* Descartar sesión con confirmación */ + _descartarSesion(sesId) { + const s = _sesiones.find(x => x.id === sesId); + const nam = s ? `"${s.chatName}"` : 'este agendamiento'; + if (!confirm(`¿Descartar el agendamiento en curso de ${nam}? Se perderán los datos.`)) return; + _sesiones = _sesiones.filter(x => x.id !== sesId); + labDomicilio._renderPills(); }, }; @@ -7537,5 +7643,8 @@ const citasModal = (() => {
+ +
+ \ No newline at end of file