up
This commit is contained in:
@@ -0,0 +1,988 @@
|
||||
<?php
|
||||
/**
|
||||
* enfermero_portal.php — Portal exclusivo para enfermeros.
|
||||
* Muestra solo los domicilios asignados al enfermero autenticado.
|
||||
*/
|
||||
require_once 'config/config.php';
|
||||
|
||||
// Solo enfermeros (o admins visualizando el portal)
|
||||
if (!isUserLoggedIn()) {
|
||||
header('Location: login.php'); exit;
|
||||
}
|
||||
|
||||
$usuario = $_SESSION['admin_user'];
|
||||
$rol = $usuario['role'] ?? 'admin';
|
||||
$enfId = (int)($usuario['enfermera_id'] ?? 0);
|
||||
|
||||
// Si es admin puede simular ver la agenda de otra enfermera vía ?eid=X
|
||||
if ($rol === 'admin' && isset($_GET['eid'])) {
|
||||
$enfId = (int)$_GET['eid'];
|
||||
}
|
||||
|
||||
if ($rol !== 'admin' && $rol !== 'enfermero') {
|
||||
header('Location: index.php'); exit;
|
||||
}
|
||||
|
||||
$hoy = date('Y-m-d');
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
|
||||
<title>Mi Agenda · Lab</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
||||
<style>
|
||||
body { background:#f4f6fb; font-size:.92rem; }
|
||||
|
||||
/* ── Top bar ── */
|
||||
.topbar { background:linear-gradient(135deg,#0d6efd,#0a58ca);
|
||||
color:#fff; padding:.75rem 1rem; position:sticky; top:0; z-index:100; }
|
||||
.topbar .btn-logout { color:rgba(255,255,255,.8); }
|
||||
|
||||
/* ── Fecha selector ── */
|
||||
.date-nav .btn { min-width:36px; }
|
||||
input[type=date].date-input { max-width:160px; border-radius:20px;
|
||||
font-weight:600; color:#0d6efd; }
|
||||
|
||||
/* ── Tarjeta domicilio ── */
|
||||
.domcard { border-left:4px solid #dee2e6; border-radius:8px;
|
||||
background:#fff; box-shadow:0 1px 4px rgba(0,0,0,.07);
|
||||
transition:box-shadow .2s; overflow:hidden; }
|
||||
.domcard:hover { box-shadow:0 3px 10px rgba(0,0,0,.12); }
|
||||
.domcard.estado-programado { border-color:#6c757d; }
|
||||
.domcard.estado-confirmado { border-color:#0d6efd; }
|
||||
.domcard.estado-en_camino { border-color:#fd7e14; }
|
||||
.domcard.estado-en_domicilio { border-color:#20c997; }
|
||||
.domcard.estado-completado { border-color:#198754; opacity:.75; }
|
||||
.domcard.estado-cancelado { border-color:#dc3545; opacity:.6; }
|
||||
|
||||
/* ── Badges estado ── */
|
||||
.badge-programado { background:#6c757d; }
|
||||
.badge-confirmado { background:#0d6efd; }
|
||||
.badge-en_camino { background:#fd7e14; }
|
||||
.badge-en_domicilio { background:#20c997; }
|
||||
.badge-completado { background:#198754; }
|
||||
.badge-cancelado { background:#dc3545; }
|
||||
|
||||
/* ── Botones acción ── */
|
||||
.btn-accion { font-size:.78rem; padding:.28rem .65rem; border-radius:20px; }
|
||||
|
||||
/* ── Servicios extra ── */
|
||||
.se-chip { font-size:.72rem; background:#f0f4ff; border:1px solid #c9d8ff;
|
||||
border-radius:16px; padding:2px 8px; color:#0d6efd; white-space:nowrap; }
|
||||
|
||||
/* ── Empty state ── */
|
||||
.empty-state { color:#adb5bd; text-align:center; padding:3rem 1rem; }
|
||||
|
||||
/* ── FAB Formulario ── */
|
||||
.fab-form { position:fixed; right:1.1rem; bottom:1.4rem; z-index:200;
|
||||
background:#6f42c1; color:#fff; width:52px; height:52px;
|
||||
border-radius:50%; border:none; box-shadow:0 4px 14px rgba(111,66,193,.4);
|
||||
font-size:1.3rem; display:flex; align-items:center; justify-content:center;
|
||||
transition:transform .15s; }
|
||||
.fab-form:hover { transform:scale(1.1); background:#5a32a3; }
|
||||
|
||||
/* ── Modal formulario (bottom-sheet en móvil) ── */
|
||||
@media (max-width:576px) {
|
||||
#modalEnvioForm .modal-dialog { margin:0; position:fixed;
|
||||
bottom:0; left:0; right:0; max-width:100%; }
|
||||
#modalEnvioForm .modal-content { border-radius:1rem 1rem 0 0; }
|
||||
}
|
||||
.form-result-box { background:#f0fff4; border:1px solid #b7ebc8;
|
||||
border-radius:8px; padding:.75rem 1rem; font-size:.85rem; }
|
||||
|
||||
/* ── FAB Nueva Agenda ── */
|
||||
.fab-agenda { position:fixed; right:1.1rem; bottom:4.8rem; z-index:200;
|
||||
background:#0d6efd; color:#fff; width:52px; height:52px;
|
||||
border-radius:50%; border:none; box-shadow:0 4px 14px rgba(13,110,253,.4);
|
||||
font-size:1.3rem; display:flex; align-items:center; justify-content:center;
|
||||
transition:transform .15s; }
|
||||
.fab-agenda:hover { transform:scale(1.1); background:#0a58ca; }
|
||||
|
||||
/* ── Sugerencias paciente ── */
|
||||
.sugerencias-pac { position:absolute; z-index:9999; background:#fff;
|
||||
border:1px solid #dee2e6; border-radius:8px;
|
||||
box-shadow:0 4px 12px rgba(0,0,0,.1); max-height:200px;
|
||||
overflow-y:auto; width:100%; top:100%; left:0; }
|
||||
.sugerencias-pac .item { padding:.5rem .75rem; cursor:pointer;
|
||||
border-bottom:1px solid #f0f0f0; }
|
||||
.sugerencias-pac .item:hover { background:#f0f4ff; }
|
||||
.sugerencias-pac .item:last-child { border-bottom:none; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- ── Top bar ───────────────────────────────────────────────────── -->
|
||||
<div class="topbar d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<div class="rounded-circle bg-white bg-opacity-25 d-flex align-items-center justify-content-center"
|
||||
style="width:36px;height:36px">
|
||||
<i class="fas fa-user-nurse text-white"></i>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fw-bold lh-1" id="enf-nombre">Cargando…</div>
|
||||
<small class="opacity-75">Portal Enfermero · <?= date('d/m/Y') ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<?php if ($rol === 'admin'): ?>
|
||||
<a href="lab_agenda_admin.php" class="btn btn-sm btn-light text-primary">
|
||||
<i class="fas fa-tachometer-alt me-1"></i>Admin
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="logout.php" class="btn btn-sm btn-logout">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- ── Navegación de fecha ──────────────────────────────────────── -->
|
||||
<div class="container-fluid px-3 py-2">
|
||||
<div class="d-flex align-items-center justify-content-between gap-2 date-nav">
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="portal.cambiarFecha(-1)">
|
||||
<i class="fas fa-chevron-left"></i>
|
||||
</button>
|
||||
<input type="date" class="form-control form-control-sm date-input text-center"
|
||||
id="portal-fecha" value="<?= $hoy ?>"
|
||||
onchange="portal.cargar()">
|
||||
<button class="btn btn-outline-secondary btn-sm" onclick="portal.cambiarFecha(1)">
|
||||
<i class="fas fa-chevron-right"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-primary btn-sm" onclick="portal.irHoy()" title="Hoy">
|
||||
<i class="fas fa-calendar-day"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Resumen del día -->
|
||||
<div id="portal-resumen" class="d-flex gap-2 mt-2 flex-wrap" style="font-size:.75rem;"></div>
|
||||
</div>
|
||||
|
||||
<!-- ── Lista de domicilios ─────────────────────────────────────── -->
|
||||
<div class="container-fluid px-3 pb-4" id="portal-lista">
|
||||
<div class="empty-state">
|
||||
<div style="font-size:2.5rem">📋</div>
|
||||
<p class="mt-2">Cargando agenda…</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════ MODAL: Confirmar cancel ══════════════════ -->
|
||||
<div class="modal fade" id="modalCancelar" tabindex="-1">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-danger text-white py-2">
|
||||
<h6 class="modal-title mb-0"><i class="fas fa-ban me-1"></i>Cancelar domicilio</h6>
|
||||
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="cancel-dom-id">
|
||||
<label class="form-label small fw-semibold">Motivo de cancelación <span class="text-danger">*</span></label>
|
||||
<textarea class="form-control form-control-sm" id="cancel-notas" rows="3"
|
||||
placeholder="Explica brevemente el motivo…"></textarea>
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
<button class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Atrás</button>
|
||||
<button class="btn btn-danger btn-sm" onclick="portal.confirmarCancelacion()">
|
||||
<i class="fas fa-ban me-1"></i>Cancelar servicio
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ═══════════════════════ MODAL: Servicio extra ═══════════════════ -->
|
||||
<div class="modal fade" id="modalServicioExtra" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-purple text-white py-2" style="background:#6f42c1">
|
||||
<h6 class="modal-title mb-0"><i class="fas fa-plus-circle me-1"></i>Servicio adicional</h6>
|
||||
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="se-dom-id">
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-semibold">Tipo <span class="text-danger">*</span></label>
|
||||
<select class="form-select form-select-sm" id="se-tipo">
|
||||
<option value="inyeccion">💉 Inyección</option>
|
||||
<option value="cura">🩹 Cura / Curación</option>
|
||||
<option value="nebulizacion">💨 Nebulización</option>
|
||||
<option value="toma_muestra">🧪 Toma de muestra</option>
|
||||
<option value="tension_arterial">🩺 Tensión arterial</option>
|
||||
<option value="glucometria">🩸 Glucometría</option>
|
||||
<option value="otro">📋 Otro</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-semibold">Descripción <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control form-control-sm" id="se-descripcion"
|
||||
placeholder="Ej: Ampicilina 500mg IM">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Notas adicionales</label>
|
||||
<textarea class="form-control form-control-sm" id="se-notas" rows="2"
|
||||
placeholder="Observaciones, resultado, indicaciones…"></textarea>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="se-pago">
|
||||
<label class="form-check-label small" for="se-pago">Tiene costo adicional</label>
|
||||
</div>
|
||||
<div id="se-valor-cont" class="mt-2" style="display:none">
|
||||
<input type="number" class="form-control form-control-sm" id="se-valor"
|
||||
placeholder="Valor en pesos" min="0" step="1000">
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
<button class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button class="btn btn-sm text-white" style="background:#6f42c1"
|
||||
onclick="portal.guardarServicioExtra()">
|
||||
<i class="fas fa-save me-1"></i>Guardar servicio
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
const ENFERMERA_ID = <?= (int)$enfId ?>;
|
||||
const ROL = '<?= $rol ?>';
|
||||
|
||||
// Etiquetas descriptivas
|
||||
const ESTADOS_LABEL = {
|
||||
programado: { label:'Programado', icon:'🕐', cls:'badge-programado' },
|
||||
confirmado: { label:'Confirmado', icon:'✅', cls:'badge-confirmado' },
|
||||
en_camino: { label:'En camino', icon:'🚗', cls:'badge-en_camino' },
|
||||
en_domicilio: { label:'En domicilio', icon:'🏠', cls:'badge-en_domicilio' },
|
||||
completado: { label:'Completado', icon:'🎉', cls:'badge-completado' },
|
||||
cancelado: { label:'Cancelado', icon:'❌', cls:'badge-cancelado' },
|
||||
};
|
||||
|
||||
// Transiciones que el enfermero puede hacer
|
||||
const TRANSICIONES = {
|
||||
programado: [{ estado:'confirmado', label:'✅ Confirmar con cliente', color:'primary' }],
|
||||
confirmado: [{ estado:'en_camino', label:'🚗 Salir hacia domicilio', color:'warning' }],
|
||||
en_camino: [{ estado:'en_domicilio', label:'🏠 Llegué al domicilio', color:'success' }],
|
||||
en_domicilio: [{ estado:'completado', label:'🎉 Marcar completado', color:'success' }],
|
||||
};
|
||||
|
||||
const portal = {
|
||||
_agenda: [],
|
||||
_cancelModal: null,
|
||||
_seModal: null,
|
||||
|
||||
init() {
|
||||
this._cancelModal = new bootstrap.Modal('#modalCancelar');
|
||||
this._seModal = new bootstrap.Modal('#modalServicioExtra');
|
||||
document.getElementById('se-pago').addEventListener('change', e => {
|
||||
document.getElementById('se-valor-cont').style.display = e.target.checked ? '' : 'none';
|
||||
});
|
||||
this.cargar();
|
||||
},
|
||||
|
||||
async cargar() {
|
||||
const fecha = document.getElementById('portal-fecha').value;
|
||||
const url = `api/lab/my_agenda.php?fecha=${fecha}` +
|
||||
(ENFERMERA_ID ? `&enfermera_id=${ENFERMERA_ID}` : '');
|
||||
|
||||
document.getElementById('portal-lista').innerHTML =
|
||||
'<div class="empty-state"><div class="spinner-border text-primary"></div><p class="mt-2">Cargando…</p></div>';
|
||||
|
||||
try {
|
||||
const r = await fetch(url);
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error al cargar');
|
||||
|
||||
this._agenda = d.agenda || [];
|
||||
|
||||
// Nombre de la enfermera (primer registro o fallback)
|
||||
if (d.agenda?.length) {
|
||||
// enriched by backend, or use session
|
||||
}
|
||||
|
||||
this._renderResumen(d.totales || {}, d.total || 0);
|
||||
this._renderLista();
|
||||
} catch (err) {
|
||||
document.getElementById('portal-lista').innerHTML =
|
||||
`<div class="alert alert-danger mx-2">${esc(err.message)}</div>`;
|
||||
}
|
||||
},
|
||||
|
||||
_renderResumen(totales, total) {
|
||||
const res = document.getElementById('portal-resumen');
|
||||
if (!total) { res.innerHTML = ''; return; }
|
||||
const chips = Object.entries(totales).map(([est, n]) => {
|
||||
const info = ESTADOS_LABEL[est] || { label: est, icon:'📌' };
|
||||
return `<span class="badge ${info.cls}" style="font-size:.72rem">
|
||||
${info.icon} ${n} ${info.label}
|
||||
</span>`;
|
||||
});
|
||||
res.innerHTML = chips.join('') + `<span class="text-muted ms-1">Total: <strong>${total}</strong></span>`;
|
||||
},
|
||||
|
||||
_renderLista() {
|
||||
const lista = document.getElementById('portal-lista');
|
||||
if (!this._agenda.length) {
|
||||
lista.innerHTML = `<div class="empty-state">
|
||||
<div style="font-size:3rem">📅</div>
|
||||
<p class="mt-2 fw-semibold">Sin servicios para este día</p>
|
||||
<small>Los servicios asignados aparecerán aquí.</small>
|
||||
</div>`;
|
||||
return;
|
||||
}
|
||||
lista.innerHTML = this._agenda.map(item => this._cardHTML(item)).join('');
|
||||
},
|
||||
|
||||
_cardHTML(item) {
|
||||
const est = item.domicilio_estado || 'programado';
|
||||
const info = ESTADOS_LABEL[est] || { label: est, icon:'📌', cls:'bg-secondary' };
|
||||
const hora = item.hora_programada ? item.hora_programada.slice(0,5) : '—';
|
||||
const trans = TRANSICIONES[est] || [];
|
||||
const seSuf = (item.servicios_extra || []).map(se =>
|
||||
`<span class="se-chip">${esc(se.descripcion)}</span>`
|
||||
).join(' ');
|
||||
|
||||
const btnsAccion = trans.map(t =>
|
||||
`<button class="btn btn-${t.color} btn-accion"
|
||||
onclick="portal.actualizarEstado(${item.domicilio_id},'${t.estado}')">
|
||||
${t.label}
|
||||
</button>`
|
||||
).join('');
|
||||
|
||||
const btnCancelar = !['completado','cancelado'].includes(est)
|
||||
? `<button class="btn btn-outline-danger btn-accion"
|
||||
onclick="portal.abrirCancelar(${item.domicilio_id})">
|
||||
❌ Cancelar
|
||||
</button>`
|
||||
: '';
|
||||
|
||||
return `<div class="domcard estado-${est} mb-3 p-3" id="card-${item.domicilio_id}">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<div>
|
||||
<span class="badge ${info.cls} text-white">${info.icon} ${info.label}</span>
|
||||
<span class="ms-1 text-muted small">🕐 ${hora}</span>
|
||||
</div>
|
||||
<small class="text-muted">#${item.domicilio_id}</small>
|
||||
</div>
|
||||
|
||||
<div class="fw-bold mb-1">
|
||||
<i class="fas fa-user me-1 text-primary"></i>${esc(item.paciente_nombre || '—')}
|
||||
</div>
|
||||
${item.paciente_telefono
|
||||
? `<a href="tel:${esc(item.paciente_telefono)}" class="d-block small text-muted mb-1">
|
||||
<i class="fas fa-phone me-1"></i>${esc(item.paciente_telefono)}
|
||||
</a>`
|
||||
: ''}
|
||||
|
||||
<div class="small text-secondary mb-1">
|
||||
<i class="fas fa-map-marker-alt me-1 text-danger"></i>${esc(item.direccion || '—')}
|
||||
${item.barrio ? `<span class="text-muted">, ${esc(item.barrio)}</span>` : ''}
|
||||
</div>
|
||||
${item.indicaciones_dir
|
||||
? `<div class="small text-muted mb-1"><i class="fas fa-info-circle me-1"></i>${esc(item.indicaciones_dir)}</div>`
|
||||
: ''}
|
||||
${item.notas_admin
|
||||
? `<div class="small text-info mb-2"><i class="fas fa-sticky-note me-1"></i>${esc(item.notas_admin)}</div>`
|
||||
: ''}
|
||||
|
||||
<!-- Servicios extra registrados -->
|
||||
${seSuf ? `<div class="d-flex flex-wrap gap-1 mb-2">${seSuf}</div>` : ''}
|
||||
|
||||
<!-- Acciones -->
|
||||
<div class="d-flex flex-wrap gap-2 mt-2">
|
||||
${btnsAccion}
|
||||
${btnCancelar}
|
||||
${!['completado','cancelado'].includes(est)
|
||||
? `<button class="btn btn-outline-secondary btn-accion"
|
||||
onclick="portal.abrirServicioExtra(${item.domicilio_id})">
|
||||
<i class="fas fa-plus me-1"></i>Servicio extra
|
||||
</button>` : ''}
|
||||
${item.paciente_telefono
|
||||
? `<a class="btn btn-outline-success btn-accion"
|
||||
href="https://wa.me/${item.paciente_telefono.replace(/\D/g,'')}" target="_blank">
|
||||
<i class="fab fa-whatsapp"></i>
|
||||
</a>` : ''}
|
||||
<button class="btn btn-outline-secondary btn-accion btn-form-envio"
|
||||
style="color:#6f42c1;border-color:#6f42c1"
|
||||
data-dom-id="${item.domicilio_id}"
|
||||
data-nombre="${esc(item.paciente_nombre||'')}"
|
||||
data-telefono="${esc(item.paciente_telefono||'')}"
|
||||
data-pac-id="${item.paciente_id||0}">
|
||||
<i class="fas fa-file-medical me-1"></i>Formulario
|
||||
</button>
|
||||
</div>
|
||||
</div>`;
|
||||
},
|
||||
|
||||
// ── Cambiar estado ─────────────────────────────────────────────────────
|
||||
async actualizarEstado(domId, nuevoEstado, notas = '') {
|
||||
try {
|
||||
const r = await fetch('api/lab/update_domicilio_enfermero.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ domicilio_id: domId, nuevo_estado: nuevoEstado, notas }),
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error);
|
||||
// Actualizar estado en memoria y re-render
|
||||
const item = this._agenda.find(a => a.domicilio_id == domId);
|
||||
if (item) item.domicilio_estado = nuevoEstado;
|
||||
this._renderLista();
|
||||
this._renderResumen(
|
||||
Object.fromEntries(
|
||||
Object.entries(
|
||||
this._agenda.reduce((a, i) => {
|
||||
a[i.domicilio_estado] = (a[i.domicilio_estado]||0)+1;
|
||||
return a;
|
||||
}, {})
|
||||
)
|
||||
),
|
||||
this._agenda.length
|
||||
);
|
||||
} catch (err) {
|
||||
alert('Error: ' + err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// ── Modal cancelar ────────────────────────────────────────────────────
|
||||
abrirCancelar(domId) {
|
||||
document.getElementById('cancel-dom-id').value = domId;
|
||||
document.getElementById('cancel-notas').value = '';
|
||||
this._cancelModal.show();
|
||||
},
|
||||
async confirmarCancelacion() {
|
||||
const domId = +document.getElementById('cancel-dom-id').value;
|
||||
const notas = document.getElementById('cancel-notas').value.trim();
|
||||
if (!notas) { document.getElementById('cancel-notas').focus(); return; }
|
||||
this._cancelModal.hide();
|
||||
await this.actualizarEstado(domId, 'cancelado', notas);
|
||||
},
|
||||
|
||||
// ── Modal servicio extra ──────────────────────────────────────────────
|
||||
abrirServicioExtra(domId) {
|
||||
document.getElementById('se-dom-id').value = domId;
|
||||
document.getElementById('se-tipo').value = 'otro';
|
||||
document.getElementById('se-descripcion').value = '';
|
||||
document.getElementById('se-notas').value = '';
|
||||
document.getElementById('se-pago').checked = false;
|
||||
document.getElementById('se-valor').value = '';
|
||||
document.getElementById('se-valor-cont').style.display = 'none';
|
||||
this._seModal.show();
|
||||
},
|
||||
async guardarServicioExtra() {
|
||||
const domId = +document.getElementById('se-dom-id').value;
|
||||
const desc = document.getElementById('se-descripcion').value.trim();
|
||||
if (!desc) { document.getElementById('se-descripcion').focus(); return; }
|
||||
|
||||
const datos = {
|
||||
domicilio_id: domId,
|
||||
tipo: document.getElementById('se-tipo').value,
|
||||
descripcion: desc,
|
||||
notas: document.getElementById('se-notas').value.trim() || null,
|
||||
requiere_pago:document.getElementById('se-pago').checked ? 1 : 0,
|
||||
valor: document.getElementById('se-pago').checked
|
||||
? parseFloat(document.getElementById('se-valor').value) || null
|
||||
: null,
|
||||
};
|
||||
try {
|
||||
const r = await fetch('api/lab/save_servicio_extra.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(datos),
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error);
|
||||
this._seModal.hide();
|
||||
// Agregar servicio extra al item en memoria
|
||||
const item = this._agenda.find(a => a.domicilio_id == domId);
|
||||
if (item) {
|
||||
item.servicios_extra = item.servicios_extra || [];
|
||||
item.servicios_extra.push({ descripcion: datos.descripcion, id: d.id });
|
||||
}
|
||||
this._renderLista();
|
||||
} catch (err) {
|
||||
alert('Error: ' + err.message);
|
||||
}
|
||||
},
|
||||
|
||||
// ── Navegación ──────────────────────────────────────────────────────
|
||||
cambiarFecha(dias) {
|
||||
const input = document.getElementById('portal-fecha');
|
||||
const d = new Date(input.value + 'T12:00:00'); // evitar offset tz
|
||||
d.setDate(d.getDate() + dias);
|
||||
input.value = d.toISOString().slice(0, 10);
|
||||
this.cargar();
|
||||
},
|
||||
irHoy() {
|
||||
document.getElementById('portal-fecha').value = '<?= $hoy ?>';
|
||||
this.cargar();
|
||||
},
|
||||
};
|
||||
|
||||
// Escapar HTML
|
||||
function esc(s) {
|
||||
return String(s||'').replace(/[<>&"']/g, c =>
|
||||
({ '<':'<','>':'>','&':'&','"':'"',"'":''' }[c]));
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
portal.init();
|
||||
agendaNueva.init();
|
||||
formEnvio.cargarPlantillas();
|
||||
|
||||
// Delegated: botones Formulario generados dinámicamente
|
||||
document.getElementById('portal-lista').addEventListener('click', e => {
|
||||
const btn = e.target.closest('.btn-form-envio');
|
||||
if (!btn) return;
|
||||
formEnvio.abrirDesdeCard(
|
||||
+btn.dataset.domId,
|
||||
btn.dataset.nombre || '',
|
||||
btn.dataset.telefono || '',
|
||||
+btn.dataset.pacId || 0
|
||||
);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!-- ══════════════ FAB: Nueva Agenda ══════════════ -->
|
||||
<button class="fab-agenda" title="Agendar nuevo domicilio" onclick="agendaNueva.abrir()">
|
||||
<i class="fas fa-calendar-plus"></i>
|
||||
</button>
|
||||
|
||||
<!-- ══════════════ FAB: Enviar formulario ══════════════ -->
|
||||
<button class="fab-form" title="Enviar formulario a paciente" onclick="formEnvio.abrirLibre()">
|
||||
<i class="fas fa-file-medical"></i>
|
||||
</button>
|
||||
|
||||
<!-- ═══════════════════════ MODAL: Nueva Agenda ══════════════════ -->
|
||||
<div class="modal fade" id="modalNuevaAgenda" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header bg-primary text-white py-2">
|
||||
<h6 class="modal-title mb-0"><i class="fas fa-calendar-plus me-2"></i>Nueva Agenda de Domicilio</h6>
|
||||
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- Buscar paciente -->
|
||||
<div class="mb-3 position-relative">
|
||||
<label class="form-label small fw-semibold">Paciente <span class="text-danger">*</span></label>
|
||||
<input type="hidden" id="na-paciente-id">
|
||||
<input type="text" id="na-paciente-buscar" class="form-control form-control-sm"
|
||||
placeholder="Buscar por nombre o teléfono…" autocomplete="off">
|
||||
<div id="na-sugerencias" class="sugerencias-pac d-none"></div>
|
||||
<div id="na-paciente-elegido" class="d-none mt-1">
|
||||
<span class="badge bg-success" id="na-paciente-label"></span>
|
||||
<button type="button" class="btn btn-link btn-sm p-0 ms-1 text-danger"
|
||||
onclick="agendaNueva.limpiarPaciente()">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Dirección -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-semibold">Dirección <span class="text-danger">*</span></label>
|
||||
<input type="text" id="na-direccion" class="form-control form-control-sm"
|
||||
placeholder="Calle 123 # 45-67">
|
||||
</div>
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Barrio</label>
|
||||
<input type="text" id="na-barrio" class="form-control form-control-sm" placeholder="Barrio">
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<label class="form-label small">Ciudad</label>
|
||||
<input type="text" id="na-ciudad" class="form-control form-control-sm" placeholder="Ciudad">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Indicaciones de dirección</label>
|
||||
<input type="text" id="na-indicaciones" class="form-control form-control-sm"
|
||||
placeholder="Casa azul, portón negro…">
|
||||
</div>
|
||||
|
||||
<!-- Fecha y hora -->
|
||||
<div class="row g-2 mb-3">
|
||||
<div class="col-7">
|
||||
<label class="form-label small fw-semibold">Fecha <span class="text-danger">*</span></label>
|
||||
<input type="date" id="na-fecha" class="form-control form-control-sm">
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<label class="form-label small">Hora</label>
|
||||
<input type="time" id="na-hora" class="form-control form-control-sm">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tipo de servicio -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label small">Tipo de servicio</label>
|
||||
<select id="na-tipo" class="form-select form-select-sm">
|
||||
<option value="">— Sin especificar —</option>
|
||||
<option value="toma_muestras">🧪 Toma de muestras</option>
|
||||
<option value="inyeccion">💉 Inyección</option>
|
||||
<option value="cura">🩹 Cura / Curación</option>
|
||||
<option value="nebulizacion">💨 Nebulización</option>
|
||||
<option value="tension_arterial">🩺 Tensión arterial</option>
|
||||
<option value="glucometria">🩸 Glucometría</option>
|
||||
<option value="otro">📋 Otro</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Notas -->
|
||||
<div class="mb-2">
|
||||
<label class="form-label small">Notas / instrucciones</label>
|
||||
<textarea id="na-notas" class="form-control form-control-sm" rows="2"
|
||||
placeholder="Instrucciones adicionales para el servicio…"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="na-error" class="alert alert-danger py-2 d-none small"></div>
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
<button class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button class="btn btn-primary btn-sm" id="na-btn-guardar" onclick="agendaNueva.guardar()">
|
||||
<i class="fas fa-calendar-check me-1"></i>Agendar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════ MODAL: Enviar Formulario ══════════════ -->
|
||||
<div class="modal fade" id="modalEnvioForm" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-white" style="background:#6f42c1">
|
||||
<h6 class="modal-title"><i class="fas fa-file-medical me-2"></i>Enviar Formulario</h6>
|
||||
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- Seleccionar plantilla -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold small">Plantilla de formulario *</label>
|
||||
<select id="fm-plantilla" class="form-select form-select-sm">
|
||||
<option value="">— Seleccionar —</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- Datos del paciente -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold small">Nombre del paciente</label>
|
||||
<input type="text" id="fm-nombre" class="form-control form-control-sm" placeholder="Nombre completo">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold small">Teléfono (para enviar por WhatsApp)</label>
|
||||
<input type="tel" id="fm-telefono" class="form-control form-control-sm" placeholder="573001234567">
|
||||
<div class="form-text">Ingrese el número con código de país, ej: 573001234567</div>
|
||||
</div>
|
||||
|
||||
<!-- Resultado -->
|
||||
<div id="fm-resultado" class="d-none">
|
||||
<div class="form-result-box">
|
||||
<div class="fw-semibold text-success mb-1">✅ Formulario generado</div>
|
||||
<div class="small mb-2">Comparte este enlace con el paciente:</div>
|
||||
<div class="input-group input-group-sm mb-2">
|
||||
<input type="text" id="fm-url" class="form-control" readonly>
|
||||
<button class="btn btn-outline-secondary" onclick="formEnvio.copiarUrl()"><i class="fas fa-copy"></i></button>
|
||||
</div>
|
||||
<a id="fm-wa-link" href="#" target="_blank" class="btn btn-success btn-sm w-100">
|
||||
<i class="fab fa-whatsapp me-1"></i>Abrir en WhatsApp
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer py-2">
|
||||
<button class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cerrar</button>
|
||||
<button id="fm-btn-enviar" class="btn btn-sm text-white" style="background:#6f42c1"
|
||||
onclick="formEnvio.enviar()">
|
||||
<i class="fas fa-paper-plane me-1"></i>Generar enlace
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// agendaNueva — Agendar domicilio desde el portal enfermero
|
||||
// ═══════════════════════════════════════════════════════
|
||||
const agendaNueva = {
|
||||
_modal: null,
|
||||
_buscarTimer: null,
|
||||
|
||||
init() {
|
||||
this._modal = new bootstrap.Modal('#modalNuevaAgenda');
|
||||
const inp = document.getElementById('na-paciente-buscar');
|
||||
inp.addEventListener('input', () => {
|
||||
clearTimeout(this._buscarTimer);
|
||||
this._buscarTimer = setTimeout(() => this._buscarPacientes(inp.value.trim()), 320);
|
||||
});
|
||||
inp.addEventListener('blur', () => {
|
||||
setTimeout(() => document.getElementById('na-sugerencias').classList.add('d-none'), 200);
|
||||
});
|
||||
},
|
||||
|
||||
abrir() {
|
||||
document.getElementById('na-paciente-id').value = '';
|
||||
document.getElementById('na-paciente-buscar').value = '';
|
||||
document.getElementById('na-direccion').value = '';
|
||||
document.getElementById('na-barrio').value = '';
|
||||
document.getElementById('na-ciudad').value = '';
|
||||
document.getElementById('na-indicaciones').value = '';
|
||||
document.getElementById('na-hora').value = '';
|
||||
document.getElementById('na-tipo').value = '';
|
||||
document.getElementById('na-notas').value = '';
|
||||
document.getElementById('na-fecha').value = document.getElementById('portal-fecha').value;
|
||||
document.getElementById('na-sugerencias').classList.add('d-none');
|
||||
document.getElementById('na-paciente-elegido').classList.add('d-none');
|
||||
document.getElementById('na-paciente-buscar').classList.remove('d-none');
|
||||
document.getElementById('na-error').classList.add('d-none');
|
||||
const btn = document.getElementById('na-btn-guardar');
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-calendar-check me-1"></i>Agendar';
|
||||
this._modal.show();
|
||||
},
|
||||
|
||||
async _buscarPacientes(q) {
|
||||
if (q.length < 2) {
|
||||
document.getElementById('na-sugerencias').classList.add('d-none');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const r = await fetch(`api/lab/get_pacientes.php?busqueda=${encodeURIComponent(q)}&limit=8`);
|
||||
const d = await r.json();
|
||||
const pac = d.data || d.pacientes || [];
|
||||
const sug = document.getElementById('na-sugerencias');
|
||||
if (!pac.length) { sug.classList.add('d-none'); return; }
|
||||
sug.innerHTML = pac.map(p =>
|
||||
`<div class="item"
|
||||
data-id="${p.id}"
|
||||
data-nombre="${esc(p.nombre_completo || p.nombre || '')}"
|
||||
data-telefono="${esc(p.telefono || '')}"
|
||||
data-direccion="${esc(p.direccion || '')}"
|
||||
data-barrio="${esc(p.barrio || '')}"
|
||||
data-ciudad="${esc(p.ciudad || '')}">
|
||||
<strong>${esc(p.nombre_completo || p.nombre || '')}</strong>
|
||||
${p.telefono ? `<small class="text-muted ms-1">${esc(p.telefono)}</small>` : ''}
|
||||
${p.direccion ? `<br><small class="text-primary"><i class="fas fa-map-marker-alt me-1"></i>${esc(p.direccion)}${p.barrio ? ', '+esc(p.barrio) : ''}</small>` : ''}
|
||||
</div>`
|
||||
).join('');
|
||||
sug.querySelectorAll('.item').forEach(el => {
|
||||
el.addEventListener('click', () => {
|
||||
this._seleccionarPaciente(
|
||||
+el.dataset.id,
|
||||
el.dataset.nombre,
|
||||
el.dataset.telefono,
|
||||
el.dataset.direccion,
|
||||
el.dataset.barrio,
|
||||
el.dataset.ciudad
|
||||
);
|
||||
});
|
||||
});
|
||||
sug.classList.remove('d-none');
|
||||
} catch(e) { /* silencioso */ }
|
||||
},
|
||||
|
||||
_seleccionarPaciente(id, nombre, telefono, direccion, barrio = '', ciudad = '') {
|
||||
document.getElementById('na-paciente-id').value = id;
|
||||
document.getElementById('na-paciente-buscar').value = nombre;
|
||||
document.getElementById('na-paciente-label').textContent =
|
||||
nombre + (telefono ? ` · ${telefono}` : '');
|
||||
document.getElementById('na-paciente-elegido').classList.remove('d-none');
|
||||
document.getElementById('na-sugerencias').classList.add('d-none');
|
||||
// Siempre rellenar la dirección del paciente (editable)
|
||||
if (direccion) document.getElementById('na-direccion').value = direccion;
|
||||
if (barrio) document.getElementById('na-barrio').value = barrio;
|
||||
if (ciudad) document.getElementById('na-ciudad').value = ciudad;
|
||||
if (direccion) document.getElementById('na-direccion').focus();
|
||||
},
|
||||
|
||||
limpiarPaciente() {
|
||||
document.getElementById('na-paciente-id').value = '';
|
||||
document.getElementById('na-paciente-buscar').value = '';
|
||||
document.getElementById('na-paciente-elegido').classList.add('d-none');
|
||||
document.getElementById('na-paciente-buscar').focus();
|
||||
},
|
||||
|
||||
async guardar() {
|
||||
const pacId = +document.getElementById('na-paciente-id').value;
|
||||
const direccion = document.getElementById('na-direccion').value.trim();
|
||||
const fecha = document.getElementById('na-fecha').value;
|
||||
document.getElementById('na-error').classList.add('d-none');
|
||||
|
||||
if (!pacId) { this._mostrarError('Selecciona un paciente de la lista.'); return; }
|
||||
if (!direccion) { this._mostrarError('La dirección es obligatoria.'); return; }
|
||||
if (!fecha) { this._mostrarError('La fecha es obligatoria.'); return; }
|
||||
|
||||
const btn = document.getElementById('na-btn-guardar');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span>Guardando…';
|
||||
|
||||
const datos = {
|
||||
paciente_id: pacId,
|
||||
direccion,
|
||||
barrio: document.getElementById('na-barrio').value.trim() || null,
|
||||
ciudad: document.getElementById('na-ciudad').value.trim() || null,
|
||||
indicaciones_dir: document.getElementById('na-indicaciones').value.trim() || null,
|
||||
fecha_programada: fecha,
|
||||
hora_programada: document.getElementById('na-hora').value || null,
|
||||
tipo_servicio: document.getElementById('na-tipo').value || null,
|
||||
notas_admin: document.getElementById('na-notas').value.trim() || null,
|
||||
estado: 'programado',
|
||||
...(ENFERMERA_ID ? { enfermera_id: ENFERMERA_ID } : {}),
|
||||
};
|
||||
|
||||
try {
|
||||
const r = await fetch('api/lab/save_domicilio.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(datos),
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error al guardar');
|
||||
this._modal.hide();
|
||||
portal.cargar();
|
||||
} catch (e) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-calendar-check me-1"></i>Agendar';
|
||||
this._mostrarError(e.message);
|
||||
}
|
||||
},
|
||||
|
||||
_mostrarError(msg) {
|
||||
const el = document.getElementById('na-error');
|
||||
el.textContent = msg;
|
||||
el.classList.remove('d-none');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// ═══════════════════════════════════════════════════════
|
||||
// formEnvio — Lógica de envío de formularios
|
||||
// ═══════════════════════════════════════════════════════
|
||||
const formEnvio = {
|
||||
_plantillas: [],
|
||||
_modalEl: null,
|
||||
_modal: null,
|
||||
_domicilioId: null,
|
||||
_pacienteId: null,
|
||||
|
||||
// ── Obtener instancia Bootstrap Modal ────────────────
|
||||
_getModal() {
|
||||
if (!this._modal) {
|
||||
this._modalEl = document.getElementById('modalEnvioForm');
|
||||
this._modal = bootstrap.Modal.getOrCreateInstance(this._modalEl);
|
||||
}
|
||||
return this._modal;
|
||||
},
|
||||
|
||||
// ── Cargar plantillas al inicio ───────────────────────
|
||||
async cargarPlantillas() {
|
||||
try {
|
||||
const r = await fetch('api/lab/get_formularios.php');
|
||||
const d = await r.json();
|
||||
this._plantillas = d.data || [];
|
||||
const sel = document.getElementById('fm-plantilla');
|
||||
this._plantillas.forEach(f => {
|
||||
const o = document.createElement('option');
|
||||
o.value = f.id;
|
||||
o.textContent = f.nombre + (f.categoria ? ` (${f.categoria})` : '');
|
||||
sel.appendChild(o);
|
||||
});
|
||||
} catch(e) {
|
||||
console.warn('No se pudieron cargar formularios:', e);
|
||||
}
|
||||
},
|
||||
|
||||
// ── Abrir modal desde botón en tarjeta de domicilio ──
|
||||
abrirDesdeCard(domId, nombre, telefono, pacienteId) {
|
||||
this._limpiar();
|
||||
this._domicilioId = domId;
|
||||
this._pacienteId = pacienteId || null;
|
||||
if (nombre) document.getElementById('fm-nombre').value = nombre;
|
||||
if (telefono) document.getElementById('fm-telefono').value = telefono;
|
||||
this._getModal().show();
|
||||
},
|
||||
|
||||
// ── Abrir modal en blanco (FAB) ────────────────────────
|
||||
abrirLibre() {
|
||||
this._limpiar();
|
||||
this._getModal().show();
|
||||
},
|
||||
|
||||
// ── Limpiar estado modal ───────────────────────────────
|
||||
_limpiar() {
|
||||
this._domicilioId = null;
|
||||
this._pacienteId = null;
|
||||
document.getElementById('fm-plantilla').value = '';
|
||||
document.getElementById('fm-nombre').value = '';
|
||||
document.getElementById('fm-telefono').value = '';
|
||||
document.getElementById('fm-resultado').classList.add('d-none');
|
||||
document.getElementById('fm-btn-enviar').disabled = false;
|
||||
},
|
||||
|
||||
// ── Generar y enviar formulario ──────────────────────
|
||||
async enviar() {
|
||||
const plantillaId = parseInt(document.getElementById('fm-plantilla').value);
|
||||
const nombre = document.getElementById('fm-nombre').value.trim();
|
||||
const telefono = document.getElementById('fm-telefono').value.trim();
|
||||
|
||||
if (!plantillaId) { alert('Selecciona una plantilla de formulario.'); return; }
|
||||
|
||||
const btn = document.getElementById('fm-btn-enviar');
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
|
||||
|
||||
try {
|
||||
const body = {
|
||||
formulario_id: plantillaId,
|
||||
domicilio_id: this._domicilioId,
|
||||
paciente_id: this._pacienteId,
|
||||
enviado_via: 'whatsapp',
|
||||
datos_prefilled: {
|
||||
...(nombre ? { nombre_completo: nombre } : {}),
|
||||
...(telefono ? { telefono: telefono } : {}),
|
||||
}
|
||||
};
|
||||
// Si hay paciente_id, dejamos que el servidor enriquezca automáticamente
|
||||
if (!this._pacienteId && nombre) {
|
||||
body.datos_prefilled.__paciente = { nombre_completo: nombre, telefono };
|
||||
}
|
||||
|
||||
const r = await fetch('api/lab/send_formulario.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const res = await r.json();
|
||||
|
||||
if (!res.ok) throw new Error(res.error || 'Error al generar');
|
||||
|
||||
// Mostrar resultado
|
||||
document.getElementById('fm-url').value = res.url;
|
||||
const waLink = document.getElementById('fm-wa-link');
|
||||
waLink.href = res.whatsapp_url || '#';
|
||||
if (!res.whatsapp_url) waLink.style.display = 'none';
|
||||
document.getElementById('fm-resultado').classList.remove('d-none');
|
||||
|
||||
btn.innerHTML = '<i class="fas fa-check me-1"></i>Listo';
|
||||
} catch(e) {
|
||||
alert('Error: ' + e.message);
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = '<i class="fas fa-paper-plane me-1"></i>Generar enlace';
|
||||
}
|
||||
},
|
||||
|
||||
// ── Copiar URL al portapapeles ─────────────────────────
|
||||
async copiarUrl() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(document.getElementById('fm-url').value);
|
||||
const btn = document.querySelector('#modalEnvioForm .input-group .btn');
|
||||
btn.innerHTML = '<i class="fas fa-check text-success"></i>';
|
||||
setTimeout(() => btn.innerHTML = '<i class="fas fa-copy"></i>', 1500);
|
||||
} catch(e) { alert('No se pudo copiar'); }
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user