feat: sesiones minimizadas múltiples — píldoras con nombre de chat y navegación
This commit is contained in:
+220
-111
@@ -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'])) {
|
||||
<small id="labdom-header-pac" class="opacity-75"></small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<button id="labdom-btn-minimizar" title="Minimizar — sigue agendando mientras copias datos"
|
||||
<button id="labdom-btn-minimizar" title="Minimizar — guarda el formulario y crea una píldora en la barra inferior"
|
||||
onclick="labDomicilio.minimizar()">
|
||||
<i class="fas fa-window-minimize"></i> Minimizar
|
||||
</button>
|
||||
@@ -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 `
|
||||
<div class="labdom-pill" onclick="labDomicilio._restaurarSesion(${s.id})"
|
||||
title="Restaurar — ${escLab(s.chatName)}${s.pacNombre ? ' · ' + escLab(s.pacNombre) : ''}">
|
||||
<span class="labdom-pill-num">${num}</span>
|
||||
<span class="labdom-pill-body">
|
||||
<span class="labdom-pill-chat"><i class="fas fa-comments me-1" style="font-size:.65rem;opacity:.75"></i>${escLab(s.chatName)}</span>
|
||||
${s.pacNombre ? `<span class="labdom-pill-pac"><i class="fas fa-user me-1" style="font-size:.6rem;opacity:.7"></i>${escLab(s.pacNombre)}</span>` : `<span class="labdom-pill-pac" style="opacity:.65">Sin paciente aún</span>`}
|
||||
</span>
|
||||
<button type="button" class="labdom-pill-close"
|
||||
onclick="event.stopPropagation();labDomicilio._descartarSesion(${s.id})"
|
||||
title="Descartar esta sesión">✕</button>
|
||||
</div>`;
|
||||
}).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) =>
|
||||
`<button type="button" class="addr-chip" onclick="labDomicilio.usarDireccion(${i})">${escLab(d.direccion)}${d.barrio ? ', '+escLab(d.barrio):''}</button>`
|
||||
).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 = (() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Barra de sesiones minimizadas (agendamientos en paralelo) -->
|
||||
<div id="labdom-pills-bar"></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user