537 lines
26 KiB
PHP
537 lines
26 KiB
PHP
<?php
|
||
/**
|
||
* modules/registro_exams/views/orden.php
|
||
* Detalle de una orden: datos paciente, ítems, muestras y resultados.
|
||
* URL: /erp.php?m=registro_exams&v=orden&id=N
|
||
*/
|
||
|
||
require_once APP_ROOT . '/config/config.php';
|
||
|
||
$ordenId = isset($_GET['id']) ? (int) $_GET['id'] : 0;
|
||
if ($ordenId <= 0) {
|
||
header('Location: ' . BASE_URL . '/erp.php?m=registro_exams&v=lista');
|
||
exit;
|
||
}
|
||
|
||
Layout::open('Orden de Exámenes', 'fas fa-file-medical');
|
||
?>
|
||
<div class="container-fluid py-3" style="max-width: 1100px;">
|
||
|
||
<!-- Encabezado -->
|
||
<div class="d-flex flex-wrap align-items-center gap-2 mb-3">
|
||
<a href="<?= BASE_URL ?>/erp.php?m=registro_exams&v=lista" class="btn btn-sm btn-outline-secondary">
|
||
<i class="fas fa-arrow-left me-1"></i>Volver
|
||
</a>
|
||
<h4 class="mb-0" id="tituloOrden">
|
||
<i class="fas fa-file-medical me-2 text-primary"></i>
|
||
<span id="codigoOrden">Cargando…</span>
|
||
</h4>
|
||
<div class="ms-auto d-flex gap-2 flex-wrap">
|
||
<a href="<?= BASE_URL ?>/erp.php?m=registro_exams&v=etiqueta&id=<?= $ordenId ?>"
|
||
target="_blank" class="btn btn-sm btn-outline-secondary">
|
||
<i class="fas fa-print me-1"></i>Etiqueta
|
||
</a>
|
||
<button class="btn btn-sm btn-outline-success" id="btnMarcarEntregada">
|
||
<i class="fas fa-hand-holding me-1"></i>Marcar entregada
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Spinner de carga -->
|
||
<div id="spinnerOrden" class="text-center py-5">
|
||
<div class="spinner-border text-primary" role="status"></div>
|
||
<div class="mt-2 text-muted">Cargando orden…</div>
|
||
</div>
|
||
|
||
<!-- Contenido principal (oculto hasta carga) -->
|
||
<div id="contenidoOrden" class="d-none">
|
||
|
||
<!-- Fila 1: Paciente + Datos médicos -->
|
||
<div class="row g-3 mb-3">
|
||
<!-- Card Paciente -->
|
||
<div class="col-md-5">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header bg-primary text-white py-2">
|
||
<i class="fas fa-user me-2"></i><strong>Paciente</strong>
|
||
</div>
|
||
<div class="card-body py-2">
|
||
<div class="fs-5 fw-bold" id="detPacNombre"></div>
|
||
<div class="text-muted small mb-1" id="detPacDoc"></div>
|
||
<div class="row g-1 small">
|
||
<div class="col-6"><span class="text-muted">Edad:</span> <span id="detPacEdad"></span></div>
|
||
<div class="col-6"><span class="text-muted">Género:</span> <span id="detPacGenero"></span></div>
|
||
<div class="col-12"><span class="text-muted">EPS:</span> <span id="detPacEps"></span></div>
|
||
<div class="col-12"><span class="text-muted">Tel:</span> <span id="detPacTel"></span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Card Orden -->
|
||
<div class="col-md-7">
|
||
<div class="card shadow-sm h-100">
|
||
<div class="card-header py-2">
|
||
<i class="fas fa-file-medical-alt me-2 text-secondary"></i>
|
||
<strong>Datos de la orden</strong>
|
||
<span class="ms-2" id="detBadgePrio"></span>
|
||
<span class="ms-1" id="detBadgeEstado"></span>
|
||
</div>
|
||
<div class="card-body py-2">
|
||
<div class="row g-1 small">
|
||
<div class="col-sm-6"><span class="text-muted">Médico:</span> <span id="detMedico"></span></div>
|
||
<div class="col-sm-6"><span class="text-muted">Registro:</span> <span id="detMedicoReg"></span></div>
|
||
<div class="col-12"><span class="text-muted">Diagnóstico:</span> <span id="detDiagnostico"></span></div>
|
||
<div class="col-12"><span class="text-muted">Notas:</span> <span id="detNotas"></span></div>
|
||
<div class="col-12"><span class="text-muted">Creado:</span> <span id="detFecha"></span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Fila 2: Muestras -->
|
||
<div class="card shadow-sm mb-3">
|
||
<div class="card-header d-flex align-items-center justify-content-between py-2">
|
||
<span><i class="fas fa-tint me-2 text-danger"></i><strong>Muestras</strong></span>
|
||
<button class="btn btn-sm btn-outline-danger" data-bs-toggle="modal" data-bs-target="#modalMuestra">
|
||
<i class="fas fa-plus me-1"></i>Registrar muestra
|
||
</button>
|
||
</div>
|
||
<div class="card-body p-0">
|
||
<table class="table table-sm mb-0" id="tablaMuestras">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>Tipo</th>
|
||
<th>Código barras</th>
|
||
<th>Estado</th>
|
||
<th>Tomada por</th>
|
||
<th>Hora</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tbodyMuestras">
|
||
<tr><td colspan="5" class="text-muted text-center py-3">Sin muestras registradas</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Fila 3: Ítems de examen con sección de resultados expandible -->
|
||
<div class="card shadow-sm mb-3">
|
||
<div class="card-header py-2">
|
||
<i class="fas fa-vials me-2 text-secondary"></i>
|
||
<strong>Exámenes solicitados</strong>
|
||
<span class="badge bg-secondary ms-2" id="contadorItems"></span>
|
||
</div>
|
||
<div class="card-body p-0" id="listaItems">
|
||
<div class="text-center text-muted py-3">Cargando ítems…</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- /contenidoOrden -->
|
||
</div>
|
||
|
||
<!-- ── Modal: Registrar muestra ──────────────────────────── -->
|
||
<div class="modal fade" id="modalMuestra" tabindex="-1">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header bg-danger text-white py-2">
|
||
<h6 class="modal-title"><i class="fas fa-tint me-2"></i>Registrar muestra</h6>
|
||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="mb-3">
|
||
<label class="form-label small fw-semibold">Tipo de muestra</label>
|
||
<select id="mTipoMuestra" class="form-select form-select-sm">
|
||
<option value="sangre_venosa">Sangre venosa</option>
|
||
<option value="sangre_capilar">Sangre capilar</option>
|
||
<option value="orina_24h">Orina 24 h</option>
|
||
<option value="orina_espontanea">Orina espontánea</option>
|
||
<option value="heces">Heces</option>
|
||
<option value="esputo">Esputo</option>
|
||
<option value="hisopado">Hisopado</option>
|
||
<option value="otro">Otro</option>
|
||
</select>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label small fw-semibold">Código de barras (opcional)</label>
|
||
<input type="text" id="mCodigoBarras" class="form-control form-control-sm"
|
||
placeholder="Escanear o ingresar código">
|
||
</div>
|
||
<div class="mb-0">
|
||
<label class="form-label small fw-semibold">Estado</label>
|
||
<select id="mEstadoMuestra" class="form-select form-select-sm">
|
||
<option value="tomada" selected>Tomada</option>
|
||
<option value="procesando">Procesando</option>
|
||
<option value="procesada">Procesada</option>
|
||
<option value="rechazada">Rechazada</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer py-2">
|
||
<button class="btn btn-sm btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||
<button class="btn btn-sm btn-danger" id="btnGuardarMuestra">
|
||
<i class="fas fa-save me-1"></i>Guardar muestra
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ── Modal: Ingresar resultados ────────────────────────── -->
|
||
<div class="modal fade modal-xl" id="modalResultados" tabindex="-1">
|
||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||
<div class="modal-content">
|
||
<div class="modal-header bg-primary text-white py-2">
|
||
<h6 class="modal-title" id="modalResTitle"><i class="fas fa-flask me-2"></i>Ingresar resultados</h6>
|
||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<input type="hidden" id="resItemId">
|
||
<div id="tablaResultados">
|
||
<!-- Filas generadas dinámicamente -->
|
||
</div>
|
||
<button type="button" class="btn btn-sm btn-outline-secondary mt-2" id="btnAgregarFila">
|
||
<i class="fas fa-plus me-1"></i>Agregar campo
|
||
</button>
|
||
</div>
|
||
<div class="modal-footer py-2">
|
||
<button class="btn btn-sm btn-outline-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||
<button class="btn btn-sm btn-primary" id="btnGuardarResultados">
|
||
<i class="fas fa-save me-1"></i>Guardar resultados
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
(function () {
|
||
const ORDEN_ID = <?= $ordenId ?>;
|
||
const BASE = '<?= BASE_URL ?>';
|
||
const API_GET = BASE + '/modules/registro_exams/api/get_orden.php';
|
||
const API_CAM = BASE + '/modules/registro_exams/api/cambiar_estado_item.php';
|
||
const API_RES = BASE + '/modules/registro_exams/api/save_resultado.php';
|
||
|
||
let ordenData = null;
|
||
|
||
// ── Carga inicial ─────────────────────────────────────────
|
||
|
||
async function cargarOrden() {
|
||
try {
|
||
const r = await fetch(`${API_GET}?id=${ORDEN_ID}`);
|
||
const j = await r.json();
|
||
if (!j.ok) throw new Error(j.error);
|
||
ordenData = j.data;
|
||
renderOrden(j.data);
|
||
} catch (e) {
|
||
document.getElementById('spinnerOrden').innerHTML =
|
||
`<div class="alert alert-danger m-4">Error al cargar: ${escHtml(e.message)}</div>`;
|
||
}
|
||
}
|
||
|
||
function renderOrden(o) {
|
||
document.getElementById('spinnerOrden').classList.add('d-none');
|
||
document.getElementById('contenidoOrden').classList.remove('d-none');
|
||
|
||
// Encabezado
|
||
document.getElementById('codigoOrden').textContent = o.codigo;
|
||
|
||
// Paciente
|
||
document.getElementById('detPacNombre').textContent = o.nombre_completo;
|
||
document.getElementById('detPacDoc').textContent = `${o.tipo_documento} ${o.numero_documento}`;
|
||
document.getElementById('detPacEdad').textContent = o.edad !== null ? `${o.edad} años` : '—';
|
||
document.getElementById('detPacGenero').textContent = o.genero || '—';
|
||
document.getElementById('detPacEps').textContent = o.eps || '—';
|
||
document.getElementById('detPacTel').textContent = o.telefono || '—';
|
||
|
||
// Orden
|
||
document.getElementById('detBadgePrio').innerHTML = badgePrio(o.prioridad);
|
||
document.getElementById('detBadgeEstado').innerHTML = badgeEstado(o.estado);
|
||
document.getElementById('detMedico').textContent = o.medico_nombre || '—';
|
||
document.getElementById('detMedicoReg').textContent = o.medico_registro || '—';
|
||
document.getElementById('detDiagnostico').textContent = o.diagnostico || '—';
|
||
document.getElementById('detNotas').textContent = o.notas || '—';
|
||
document.getElementById('detFecha').textContent = o.creado_at ? o.creado_at.substr(0, 16) : '—';
|
||
|
||
// Muestras
|
||
renderMuestras(o.muestras);
|
||
|
||
// Ítems
|
||
renderItems(o.items);
|
||
|
||
// Contador
|
||
document.getElementById('contadorItems').textContent = o.items.length;
|
||
|
||
// Botón marcar entregada (solo si está completa)
|
||
const btnEntregar = document.getElementById('btnMarcarEntregada');
|
||
btnEntregar.disabled = !['completa'].includes(o.estado);
|
||
}
|
||
|
||
// ── Muestras ──────────────────────────────────────────────
|
||
|
||
function renderMuestras(muestras) {
|
||
const tbody = document.getElementById('tbodyMuestras');
|
||
if (!muestras.length) {
|
||
tbody.innerHTML = '<tr><td colspan="5" class="text-muted text-center py-3">Sin muestras registradas</td></tr>';
|
||
return;
|
||
}
|
||
const labelTipo = {
|
||
sangre_venosa: 'Sangre venosa', sangre_capilar: 'Sangre capilar',
|
||
orina_24h: 'Orina 24h', orina_espontanea: 'Orina espontánea',
|
||
heces: 'Heces', esputo: 'Esputo', hisopado: 'Hisopado', otro: 'Otro'
|
||
};
|
||
const badgeEst = {
|
||
pendiente:'<span class="badge bg-light text-dark border">Pendiente</span>',
|
||
tomada: '<span class="badge bg-success">Tomada</span>',
|
||
procesando:'<span class="badge bg-info text-dark">Procesando</span>',
|
||
procesada: '<span class="badge bg-primary">Procesada</span>',
|
||
rechazada: '<span class="badge bg-danger">Rechazada</span>',
|
||
};
|
||
tbody.innerHTML = muestras.map(m =>
|
||
`<tr>
|
||
<td>${labelTipo[m.tipo_muestra] || m.tipo_muestra}</td>
|
||
<td>${escHtml(m.codigo_barras || '—')}</td>
|
||
<td>${badgeEst[m.estado] || m.estado}</td>
|
||
<td>${escHtml(m.tomada_nombre || '—')}</td>
|
||
<td>${m.tomada_at ? m.tomada_at.substr(11,5) : '—'}</td>
|
||
</tr>`
|
||
).join('');
|
||
}
|
||
|
||
// ── Ítems ─────────────────────────────────────────────────
|
||
|
||
function renderItems(items) {
|
||
const cont = document.getElementById('listaItems');
|
||
if (!items.length) {
|
||
cont.innerHTML = '<div class="text-muted text-center py-3">Sin ítems</div>';
|
||
return;
|
||
}
|
||
|
||
const estadoColor = {
|
||
pendiente: 'bg-light text-dark border',
|
||
muestra_tomada: 'bg-warning text-dark',
|
||
en_proceso: 'bg-info text-dark',
|
||
resultado_listo: 'bg-success',
|
||
entregado: 'bg-secondary',
|
||
};
|
||
|
||
cont.innerHTML = items.map(item => {
|
||
const badge = `<span class="badge ${estadoColor[item.estado] || 'bg-light'}">${labelEstItem(item.estado)}</span>`;
|
||
const nResultados = item.resultados?.length ?? 0;
|
||
|
||
const resHTML = nResultados > 0 ? renderTablaResultados(item.resultados) : '';
|
||
|
||
return `
|
||
<div class="border-bottom p-3" id="item-${item.id}">
|
||
<div class="d-flex flex-wrap align-items-start gap-2">
|
||
<div class="flex-grow-1">
|
||
<div class="fw-semibold">${escHtml(item.tipo_nombre)}
|
||
<small class="text-muted ms-1">${escHtml(item.tipo_codigo)}</small>
|
||
</div>
|
||
<div class="small text-muted">${escHtml(item.categoria || '')}</div>
|
||
${item.tipo_instrucciones
|
||
? `<div class="small text-info mt-1"><i class="fas fa-info-circle me-1"></i>${escHtml(item.tipo_instrucciones)}</div>`
|
||
: ''}
|
||
</div>
|
||
<div class="d-flex align-items-center gap-1 flex-wrap">
|
||
${badge}
|
||
<button class="btn btn-sm btn-outline-primary"
|
||
onclick="abrirResultados(${item.id}, '${escAtt(item.tipo_nombre)}', ${JSON.stringify(item.resultados || []).replace(/"/g,'"')})">
|
||
<i class="fas fa-flask me-1"></i>${nResultados > 0 ? 'Editar resultados' : 'Ingresar resultados'}
|
||
</button>
|
||
<div class="dropdown">
|
||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
|
||
Estado
|
||
</button>
|
||
<ul class="dropdown-menu dropdown-menu-end">
|
||
${['pendiente','muestra_tomada','en_proceso','resultado_listo','entregado'].map(est =>
|
||
`<li><a class="dropdown-item small" href="#"
|
||
onclick="cambiarEstado('item',${item.id},'${est}');return false;">
|
||
${labelEstItem(est)}</a></li>`
|
||
).join('')}
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
${resHTML ? `<div class="mt-2">${resHTML}</div>` : ''}
|
||
</div>`;
|
||
}).join('');
|
||
}
|
||
|
||
function renderTablaResultados(resultados) {
|
||
if (!resultados.length) return '';
|
||
const rows = resultados.map(r =>
|
||
`<tr class="${r.es_anormal ? 'table-danger' : ''}">
|
||
<td>${escHtml(r.campo_nombre)}</td>
|
||
<td class="fw-semibold">${escHtml(r.valor_texto || (r.valor_numerico !== null ? r.valor_numerico : '—'))}</td>
|
||
<td>${escHtml(r.unidad || '—')}</td>
|
||
<td>${escHtml(r.referencia_texto || (r.referencia_min !== null ? `${r.referencia_min} – ${r.referencia_max}` : '—'))}</td>
|
||
<td>${r.es_anormal ? '<span class="badge bg-danger">ANORMAL</span>' : ''}</td>
|
||
</tr>`
|
||
).join('');
|
||
return `<table class="table table-sm table-bordered mb-0" style="font-size:.85rem">
|
||
<thead class="table-light">
|
||
<tr><th>Campo</th><th>Valor</th><th>Unidad</th><th>Referencia</th><th>Obs.</th></tr>
|
||
</thead>
|
||
<tbody>${rows}</tbody>
|
||
</table>`;
|
||
}
|
||
|
||
// ── Cambiar estado ────────────────────────────────────────
|
||
|
||
window.cambiarEstado = async function(tipo, id, estado) {
|
||
try {
|
||
const r = await fetch(API_CAM, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ tipo, id, estado }),
|
||
});
|
||
const j = await r.json();
|
||
if (!j.ok) throw new Error(j.error);
|
||
await cargarOrden();
|
||
} catch (e) {
|
||
alert('Error: ' + e.message);
|
||
}
|
||
};
|
||
|
||
document.getElementById('btnMarcarEntregada').addEventListener('click', async () => {
|
||
if (!confirm('¿Marcar esta orden como entregada?')) return;
|
||
await cambiarEstado('orden', ORDEN_ID, 'entregada');
|
||
});
|
||
|
||
// ── Registrar muestra ─────────────────────────────────────
|
||
|
||
document.getElementById('btnGuardarMuestra').addEventListener('click', async () => {
|
||
const btn = document.getElementById('btnGuardarMuestra');
|
||
btn.disabled = true;
|
||
try {
|
||
const r = await fetch(API_CAM, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
tipo: 'muestra',
|
||
id: null,
|
||
orden_id: ORDEN_ID,
|
||
tipo_muestra: document.getElementById('mTipoMuestra').value,
|
||
codigo_barras: document.getElementById('mCodigoBarras').value.trim(),
|
||
estado: document.getElementById('mEstadoMuestra').value,
|
||
}),
|
||
});
|
||
const j = await r.json();
|
||
if (!j.ok) throw new Error(j.error);
|
||
bootstrap.Modal.getInstance(document.getElementById('modalMuestra')).hide();
|
||
document.getElementById('mCodigoBarras').value = '';
|
||
await cargarOrden();
|
||
} catch (e) {
|
||
alert('Error: ' + e.message);
|
||
} finally {
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// ── Modal Resultados ──────────────────────────────────────
|
||
|
||
window.abrirResultados = function(itemId, nombre, resultados) {
|
||
document.getElementById('resItemId').value = itemId;
|
||
document.getElementById('modalResTitle').innerHTML =
|
||
`<i class="fas fa-flask me-2"></i>Resultados — ${escHtml(nombre)}`;
|
||
|
||
renderFilasResultados(resultados);
|
||
bootstrap.Modal.getOrCreateInstance(document.getElementById('modalResultados')).show();
|
||
};
|
||
|
||
function renderFilasResultados(resultados) {
|
||
const cont = document.getElementById('tablaResultados');
|
||
const filas = resultados.length > 0 ? resultados : [{}];
|
||
cont.innerHTML = `
|
||
<table class="table table-sm table-bordered" id="tResultados">
|
||
<thead class="table-light">
|
||
<tr>
|
||
<th>Campo</th>
|
||
<th>Valor</th>
|
||
<th>Unidad</th>
|
||
<th>Referencia</th>
|
||
<th class="text-center">Anormal</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="tbodyRes">${filas.map(r => buildFilaRes(r)).join('')}</tbody>
|
||
</table>`;
|
||
}
|
||
|
||
function buildFilaRes(r = {}) {
|
||
return `<tr>
|
||
<td><input class="form-control form-control-sm r-campo" value="${escAtt(r.campo_nombre||'')}" placeholder="Hemoglobina"></td>
|
||
<td><input class="form-control form-control-sm r-valor" value="${escAtt(r.valor_texto||r.valor_numerico||'')}" placeholder="14.5"></td>
|
||
<td><input class="form-control form-control-sm r-unidad" value="${escAtt(r.unidad||'')}" placeholder="g/dL" style="width:80px"></td>
|
||
<td><input class="form-control form-control-sm r-ref" value="${escAtt(r.referencia_texto || (r.referencia_min!=null ? r.referencia_min+' – '+r.referencia_max : ''))}" placeholder="12.0 – 16.0"></td>
|
||
<td class="text-center"><input type="checkbox" class="form-check-input r-anormal" ${r.es_anormal ? 'checked' : ''}></td>
|
||
<td><button type="button" class="btn btn-sm btn-outline-danger" onclick="this.closest('tr').remove()"><i class="fas fa-times"></i></button></td>
|
||
</tr>`;
|
||
}
|
||
|
||
document.getElementById('btnAgregarFila').addEventListener('click', () => {
|
||
document.getElementById('tbodyRes').insertAdjacentHTML('beforeend', buildFilaRes());
|
||
});
|
||
|
||
document.getElementById('btnGuardarResultados').addEventListener('click', async () => {
|
||
const itemId = parseInt(document.getElementById('resItemId').value);
|
||
const filas = [...document.querySelectorAll('#tbodyRes tr')];
|
||
|
||
const resultados = filas.map(tr => ({
|
||
campo_nombre: tr.querySelector('.r-campo').value.trim(),
|
||
valor_texto: tr.querySelector('.r-valor').value.trim(),
|
||
valor_numerico: parseFloat(tr.querySelector('.r-valor').value) || null,
|
||
unidad: tr.querySelector('.r-unidad').value.trim(),
|
||
referencia_texto: tr.querySelector('.r-ref').value.trim(),
|
||
es_anormal: tr.querySelector('.r-anormal').checked,
|
||
})).filter(r => r.campo_nombre !== '');
|
||
|
||
if (!resultados.length) { alert('Ingrese al menos un campo de resultado.'); return; }
|
||
|
||
const btn = document.getElementById('btnGuardarResultados');
|
||
btn.disabled = true;
|
||
try {
|
||
const r = await fetch(API_RES, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ item_id: itemId, resultados }),
|
||
});
|
||
const j = await r.json();
|
||
if (!j.ok) throw new Error(j.error);
|
||
bootstrap.Modal.getInstance(document.getElementById('modalResultados')).hide();
|
||
await cargarOrden();
|
||
} catch (e) {
|
||
alert('Error: ' + e.message);
|
||
} finally {
|
||
btn.disabled = false;
|
||
}
|
||
});
|
||
|
||
// ── Helpers ───────────────────────────────────────────────
|
||
|
||
function labelEstItem(e) {
|
||
return {
|
||
pendiente: 'Pendiente',
|
||
muestra_tomada: 'Muestra tomada',
|
||
en_proceso: 'En proceso',
|
||
resultado_listo: 'Resultado listo',
|
||
entregado: 'Entregado',
|
||
}[e] || e;
|
||
}
|
||
|
||
function badgePrio(p) {
|
||
return {stat:'<span class="badge bg-danger">STAT</span>',urgente:'<span class="badge bg-warning text-dark">Urgente</span>',normal:'<span class="badge bg-secondary">Normal</span>'}[p]||'';
|
||
}
|
||
function badgeEstado(e) {
|
||
return {pendiente:'<span class="badge bg-light text-dark border">Pendiente</span>',en_proceso:'<span class="badge bg-primary">En proceso</span>',completa:'<span class="badge bg-success">Completa</span>',entregada:'<span class="badge bg-info text-dark">Entregada</span>',cancelada:'<span class="badge bg-dark">Cancelada</span>'}[e]||e;
|
||
}
|
||
function escHtml(s) { return String(s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||
function escAtt(s) { return String(s||'').replace(/"/g,'"'); }
|
||
|
||
// ── Inicio ───────────────────────────────────────────────
|
||
cargarOrden();
|
||
})();
|
||
</script>
|
||
<?php Layout::close(); ?>
|