feat: 6 nuevas funcionalidades - notas obligatorias turno, reporte enfermero, plantilla WhatsApp, adjuntar orden modal, roles solo lectura, fix valores copago
This commit is contained in:
@@ -243,6 +243,55 @@ $adminNombre = $_SESSION['admin_user']['full_name'] ?? $_SESSION['admin_user']['
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════ -->
|
||||
<!-- Reporte domicilios por enfermero -->
|
||||
<!-- ══════════════════════════════════════════════════════════════ -->
|
||||
<div class="card border-0 shadow-sm mb-4">
|
||||
<div class="card-header bg-white border-0 d-flex flex-wrap gap-2 align-items-end">
|
||||
<h6 class="fw-semibold mb-0 me-auto">
|
||||
<i class="fas fa-user-nurse me-2 text-success"></i>Domicilios por enfermero
|
||||
</h6>
|
||||
<div class="d-flex flex-wrap gap-2 align-items-end">
|
||||
<div>
|
||||
<label class="form-label small text-muted text-uppercase mb-1">Año</label>
|
||||
<input type="number" id="enf-anio" class="form-control form-control-sm"
|
||||
value="<?= date('Y') ?>" min="2020" max="2099" style="width:90px">
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label small text-muted text-uppercase mb-1">Mes</label>
|
||||
<select id="enf-mes" class="form-select form-select-sm" style="width:100px">
|
||||
<option value="">Todos</option>
|
||||
<option value="1">Enero</option><option value="2">Febrero</option>
|
||||
<option value="3">Marzo</option><option value="4">Abril</option>
|
||||
<option value="5">Mayo</option><option value="6">Junio</option>
|
||||
<option value="7">Julio</option><option value="8">Agosto</option>
|
||||
<option value="9">Septiembre</option><option value="10">Octubre</option>
|
||||
<option value="11">Noviembre</option><option value="12">Diciembre</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label small text-muted text-uppercase mb-1">Enfermero/a</label>
|
||||
<select id="enf-selector" class="form-select form-select-sm" style="min-width:160px">
|
||||
<option value="">Todos</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm" onclick="cargarReporteEnfermero()">
|
||||
<i class="fas fa-sync-alt me-1"></i>Filtrar
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-success" onclick="exportarReporteEnfermero()">
|
||||
<i class="fas fa-file-excel me-1"></i>Excel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<div id="enf-tabla-wrap" style="overflow-x:auto">
|
||||
<div class="text-center py-4 text-muted">
|
||||
<i class="fas fa-spinner fa-spin"></i> Cargando…
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════════════════════════════════════════════════════════ -->
|
||||
<!-- KPIs principales -->
|
||||
<!-- ══════════════════════════════════════════════════════════════ -->
|
||||
@@ -429,6 +478,90 @@ const COLOR_EST = {
|
||||
const MODULO_COLOR = { ordenes:'warning', domicilios:'success', pacientes:'primary', enfermeras:'info', asignaciones:'secondary' };
|
||||
let logPag = 1;
|
||||
|
||||
// ── Reporte por enfermero ───────────────────────────────────────────────────
|
||||
async function cargarReporteEnfermero() {
|
||||
const wrap = document.getElementById('enf-tabla-wrap');
|
||||
wrap.innerHTML = '<div class="text-center py-4 text-muted"><i class="fas fa-spinner fa-spin"></i></div>';
|
||||
|
||||
const anio = document.getElementById('enf-anio').value;
|
||||
const mes = document.getElementById('enf-mes').value;
|
||||
const enf = document.getElementById('enf-selector').value;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (anio) params.set('anio', anio);
|
||||
if (mes) params.set('mes', mes);
|
||||
if (enf) params.set('enfermera_id', enf);
|
||||
|
||||
try {
|
||||
const r = await fetch(`api/lab/get_reporte_enfermero.php?${params}`);
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error || 'Error');
|
||||
|
||||
// Poblar selector de enfermeras (sólo la primera vez)
|
||||
const sel = document.getElementById('enf-selector');
|
||||
if (sel.options.length === 1 && d.enfermeras?.length) {
|
||||
d.enfermeras.forEach(e => {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = e.id;
|
||||
opt.textContent = e.nombre_completo;
|
||||
sel.appendChild(opt);
|
||||
});
|
||||
}
|
||||
|
||||
if (!d.filas?.length) {
|
||||
wrap.innerHTML = '<p class="text-muted text-center py-3 small">Sin datos para los filtros seleccionados.</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
wrap.innerHTML = `
|
||||
<table class="table table-sm table-hover mb-0" style="font-size:.85rem">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Enfermero/a</th>
|
||||
<th class="text-center">Período</th>
|
||||
<th class="text-center">Total</th>
|
||||
<th class="text-center text-success">Completados</th>
|
||||
<th class="text-center text-danger">Cancelados</th>
|
||||
<th class="text-center text-warning">En proceso</th>
|
||||
<th class="text-center">Tasa éxito</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${d.filas.map(f => {
|
||||
const color = f.tasa_pct >= 80 ? '#43a047' : f.tasa_pct >= 50 ? '#fb8c00' : '#e53935';
|
||||
return `<tr>
|
||||
<td class="fw-semibold">${esc(f.enfermera)}</td>
|
||||
<td class="text-center text-muted">${esc(f.mes_nombre)} ${f.anio}</td>
|
||||
<td class="text-center">${f.total}</td>
|
||||
<td class="text-center text-success">${f.completados}</td>
|
||||
<td class="text-center text-danger">${f.cancelados}</td>
|
||||
<td class="text-center text-warning">${f.en_proceso}</td>
|
||||
<td class="text-center">
|
||||
<span class="fw-bold" style="color:${color}">${f.tasa_pct}%</span>
|
||||
<div style="width:60px;height:5px;background:#f0f0f0;border-radius:3px;margin:2px auto 0">
|
||||
<div style="width:${f.tasa_pct}%;height:100%;background:${color};border-radius:3px"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>`;
|
||||
}).join('')}
|
||||
</tbody>
|
||||
</table>`;
|
||||
} catch (e) {
|
||||
wrap.innerHTML = `<div class="alert alert-danger m-3 small">${esc(e.message)}</div>`;
|
||||
}
|
||||
}
|
||||
|
||||
function exportarReporteEnfermero() {
|
||||
const anio = document.getElementById('enf-anio').value;
|
||||
const mes = document.getElementById('enf-mes').value;
|
||||
const enf = document.getElementById('enf-selector').value;
|
||||
const params = new URLSearchParams({ export: 'domicilios', desde: `${anio || '2020'}-${mes ? String(mes).padStart(2,'0') : '01'}-01` });
|
||||
if (mes) params.set('hasta', `${anio || new Date().getFullYear()}-${String(mes).padStart(2,'0')}-31`);
|
||||
else params.set('hasta', `${anio || new Date().getFullYear()}-12-31`);
|
||||
if (enf) params.set('enfermera_id', enf);
|
||||
window.open(`lab_reportes.php?${params}`, '_blank');
|
||||
}
|
||||
|
||||
// ── Carga todo en paralelo ──────────────────────────────────────────────────
|
||||
async function cargarTodo() {
|
||||
const desde = fecha('rpt-desde');
|
||||
@@ -437,6 +570,7 @@ async function cargarTodo() {
|
||||
cargarMetricas(desde, hasta),
|
||||
cargarResumenOrdenes(desde, hasta),
|
||||
cargarActividad(1),
|
||||
cargarReporteEnfermero(),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user