subir mas de un archivo, reprogramados

This commit is contained in:
Lizandro Guarnizo
2026-04-08 10:31:12 -05:00
parent 882ce56e96
commit c68a79a3a7
3 changed files with 267 additions and 230 deletions
+122 -144
View File
@@ -398,36 +398,21 @@ $domIdParam = (int)($_GET['id'] ?? 0);
</h6>
<div class="mb-3">
<input type="hidden" name="orden_id" id="dom-orden-id-form">
<!-- Preview archivo actual o pendiente -->
<div id="dom-orden-preview" class="d-flex align-items-center gap-3 p-2 rounded border bg-light mb-2 d-none">
<div id="dom-orden-thumb-wrap">
<img id="dom-orden-img" src="" alt="Orden" style="max-height:80px;max-width:100px;object-fit:contain;border-radius:4px;display:none">
<div id="dom-orden-icon" class="d-none">
<i class="fas fa-file-alt fa-3x text-secondary"></i>
</div>
</div>
<div class="flex-grow-1 overflow-hidden">
<div id="dom-orden-fname" class="small fw-semibold text-truncate"></div>
<div id="dom-orden-fsize" class="small text-muted"></div>
</div>
</div>
<!-- Lista de chips (archivos ya guardados + nuevos pendientes) -->
<div id="dom-orden-list" class="d-flex flex-wrap gap-2 mb-2"></div>
<div id="dom-orden-empty" class="small text-muted mb-2">
<i class="fas fa-paperclip me-1"></i> Sin orden adjunta
<i class="fas fa-paperclip me-1"></i> Sin archivos adjuntos
</div>
<div class="d-flex gap-2 flex-wrap align-items-center">
<label class="btn btn-sm btn-outline-primary mb-0" for="dom-orden-input">
<i class="fas fa-upload me-1"></i>
<span id="dom-orden-btn-lbl">Adjuntar orden</span>
<span id="dom-orden-btn-lbl">Adjuntar archivos</span>
</label>
<button type="button" class="btn btn-sm btn-outline-danger d-none" id="dom-orden-quitar"
onclick="domQuitarOrden()">
<i class="fas fa-times me-1"></i> Quitar
</button>
</div>
<input type="file" id="dom-orden-input" class="d-none"
accept="image/jpeg,image/png,image/gif,image/webp,application/pdf,.pdf,.doc,.docx"
onchange="domSeleccionarOrdenFile(this)">
<small class="text-muted d-block mt-1">Imagen, PDF o DOC — máx. 10 MB</small>
<input type="file" id="dom-orden-input" class="d-none" multiple
accept="image/jpeg,image/png,image/gif,image/webp,application/pdf,.pdf,.doc,.docx,.xls,.xlsx"
onchange="domSeleccionarOrdenFiles(this)">
<small class="text-muted d-block mt-1">Imágenes, PDF, DOC, XLS — máx. 10 MB por archivo</small>
</div>
<!-- ── Notas ── -->
@@ -590,91 +575,85 @@ const modal = new bootstrap.Modal('#modalDomicilio');
let _modalRespForm = null;
let _modalAsignarEnf = null;
// ── Estado orden en el modal ───────────────────────────────────────────────
let _domOrderFile = null; // File pendiente de subir
let _domCurrentLocalFile = null; // local_file actual de la orden
let _domRemoveOrder = false; // el usuario quiere desvincularse de la orden
// ── Estado órdenes en el modal (multi-archivo) ────────────────────────────
let _domNewFiles = []; // File[] nuevos pendientes de subir
let _domCurrentFiles = []; // [{orden_id, local_file}] ya guardados
let _domRemovedOrdenIds = []; // orden_ids a desvincular al guardar
function _domResetOrden() {
_domOrderFile = null;
_domCurrentLocalFile = null;
_domRemoveOrder = false;
document.getElementById('dom-orden-id-form').value = '';
document.getElementById('dom-orden-input').value = '';
_domRenderOrdenPreview();
function _domResetOrdenes() {
_domNewFiles = [];
_domCurrentFiles = [];
_domRemovedOrdenIds = [];
document.getElementById('dom-orden-input').value = '';
_domRenderOrdenes();
}
// Alias para retrocompat con llamadas _domResetOrden()
const _domResetOrden = _domResetOrdenes;
function _domRenderOrdenes() {
const list = document.getElementById('dom-orden-list');
const empty = document.getElementById('dom-orden-empty');
const btn = document.getElementById('dom-orden-btn-lbl');
const chipStyle = 'width:72px;height:72px;border:1px solid #dee2e6;border-radius:6px;background:#f8f9fa;display:flex;align-items:center;justify-content:center;overflow:hidden';
const delStyle = 'position:absolute;top:-5px;right:-5px;width:18px;height:18px;border-radius:50%;background:#dc3545;color:#fff;border:none;font-size:.7rem;cursor:pointer;display:flex;align-items:center;justify-content:center;padding:0';
list.innerHTML = '';
_domCurrentFiles.forEach((f, i) => {
const lf = f.local_file || '';
const isImg = /\.(jpe?g|png|gif|webp)$/i.test(lf);
const thumb = isImg
? `<img src="uploads/media/${encodeURIComponent(lf)}" style="width:100%;height:100%;object-fit:cover">`
: `<i class="fas fa-file-alt fa-2x text-secondary"></i>`;
const fname = lf.split('/').pop();
list.insertAdjacentHTML('beforeend', `
<div class="position-relative" style="width:72px">
<div style="${chipStyle}">${thumb}</div>
<span class="d-block text-truncate" style="font-size:.6rem;color:#666;max-width:72px" title="${esc(fname)}">${esc(fname)}</span>
<button type="button" onclick="domQuitarExistente(${i})" style="${delStyle}" title="Quitar">×</button>
</div>`);
});
_domNewFiles.forEach((f, i) => {
const isImg = f.type.startsWith('image/');
const thumb = isImg
? `<img src="${URL.createObjectURL(f)}" style="width:100%;height:100%;object-fit:cover">`
: `<i class="fas fa-file-alt fa-2x text-secondary"></i>`;
list.insertAdjacentHTML('beforeend', `
<div class="position-relative" style="width:72px">
<div style="${chipStyle}">${thumb}</div>
<span class="d-block text-truncate" style="font-size:.6rem;color:#666;max-width:72px" title="${esc(f.name)}">${esc(f.name)}</span>
<span style="font-size:.55rem;background:#ffc107;color:#000;border-radius:3px;padding:0 3px">nuevo</span>
<button type="button" onclick="domQuitarNuevo(${i})" style="${delStyle}" title="Quitar">×</button>
</div>`);
});
const total = _domCurrentFiles.length + _domNewFiles.length;
empty.classList.toggle('d-none', total > 0);
btn.textContent = total > 0 ? 'Agregar más archivos' : 'Adjuntar archivos';
}
function _domRenderOrdenPreview() {
const preview = document.getElementById('dom-orden-preview');
const empty = document.getElementById('dom-orden-empty');
const img = document.getElementById('dom-orden-img');
const icon = document.getElementById('dom-orden-icon');
const fname = document.getElementById('dom-orden-fname');
const fsize = document.getElementById('dom-orden-fsize');
const btnQuitar = document.getElementById('dom-orden-quitar');
const btnLbl = document.getElementById('dom-orden-btn-lbl');
if (_domOrderFile) {
// Archivo local seleccionado aún no subido
const isImg = _domOrderFile.type.startsWith('image/');
if (isImg) {
img.src = URL.createObjectURL(_domOrderFile);
img.style.display = '';
icon.classList.add('d-none');
} else {
img.style.display = 'none';
icon.classList.remove('d-none');
function domSeleccionarOrdenFiles(input) {
for (const file of Array.from(input.files)) {
if (file.size > 10 * 1024 * 1024) {
alert(`"${file.name}" supera los 10 MB permitidos`);
continue;
}
fname.textContent = _domOrderFile.name;
fsize.textContent = (_domOrderFile.size / 1024).toFixed(1) + ' KB — pendiente de guardar';
preview.classList.remove('d-none');
empty.classList.add('d-none');
btnQuitar.classList.remove('d-none');
btnLbl.textContent = 'Cambiar archivo';
} else if (_domCurrentLocalFile && !_domRemoveOrder) {
// Archivo ya guardado en servidor
const isImg = /\.(jpe?g|png|gif|webp)$/i.test(_domCurrentLocalFile);
if (isImg) {
img.src = 'uploads/media/' + encodeURIComponent(_domCurrentLocalFile);
img.style.display = '';
icon.classList.add('d-none');
} else {
img.style.display = 'none';
icon.classList.remove('d-none');
}
fname.textContent = _domCurrentLocalFile;
fsize.textContent = 'Archivo guardado';
preview.classList.remove('d-none');
empty.classList.add('d-none');
btnQuitar.classList.remove('d-none');
btnLbl.textContent = 'Cambiar archivo';
} else {
preview.classList.add('d-none');
empty.classList.remove('d-none');
btnQuitar.classList.add('d-none');
btnLbl.textContent = 'Adjuntar orden';
_domNewFiles.push(file);
}
input.value = '';
_domRenderOrdenes();
}
function domSeleccionarOrdenFile(input) {
const file = input.files[0];
if (!file) return;
if (file.size > 10 * 1024 * 1024) {
alert('El archivo supera los 10 MB permitidos');
input.value = '';
return;
}
_domOrderFile = file;
_domRemoveOrder = false;
_domRenderOrdenPreview();
function domQuitarExistente(i) {
const removed = _domCurrentFiles.splice(i, 1)[0];
if (removed?.orden_id) _domRemovedOrdenIds.push(removed.orden_id);
_domRenderOrdenes();
}
function domQuitarOrden() {
_domOrderFile = null;
_domRemoveOrder = true;
document.getElementById('dom-orden-id-form').value = '';
document.getElementById('dom-orden-input').value = '';
_domRenderOrdenPreview();
function domQuitarNuevo(i) {
_domNewFiles.splice(i, 1);
_domRenderOrdenes();
}
const COLOR_DOM = { programado:'secondary', confirmado:'info', en_camino:'primary', en_domicilio:'warning', completado:'success', cancelado:'danger', reprogramado:'dark' };
const PUEDE_ESCRIBIR = <?= $puedeEscribir ? 'true' : 'false' ?>;
@@ -1255,11 +1234,15 @@ async function editarDomicilio(id) {
document.getElementById('dom-copago-lab').value = dom.copago_laboratorio || '';
document.getElementById('dom-notas').value = dom.notas_admin || '';
// Orden médica adjunta
_domResetOrden();
document.getElementById('dom-orden-id-form').value = dom.orden_id || '';
_domCurrentLocalFile = dom.orden_local_file || null;
_domRenderOrdenPreview();
// Órdenes médicas adjuntas (multi-archivo)
_domResetOrdenes();
let archivosExistentes = dom.archivos_ordenes || [];
if (!archivosExistentes.length && dom.orden_local_file) {
// Retrocompat: domicilio viejo con solo orden_id FK
archivosExistentes = [{ orden_id: dom.orden_id || null, local_file: dom.orden_local_file }];
}
_domCurrentFiles = archivosExistentes;
_domRenderOrdenes();
modal.show();
}
@@ -1277,57 +1260,52 @@ async function guardarDomicilio() {
const datos = Object.fromEntries(new FormData(form).entries());
if (!datos.id) delete datos.id;
// Tomar orden_id del campo oculto real (no el parámetro URL)
datos.orden_id = document.getElementById('dom-orden-id-form').value || null;
delete datos.orden_id; // archivos se vinculan por domicilio_id; no pasamos FK
delete datos.orden_id_url;
// ── Gestión del archivo de orden médica ──────────────────────────────
if (_domRemoveOrder) {
datos.orden_id = null;
} else if (_domOrderFile) {
// Subir el archivo primero
const fd = new FormData();
fd.append('file', _domOrderFile);
const upR = await fetch('api/lab/upload_orden.php', { method:'POST', body: fd });
const upD = await upR.json();
if (!upD.success) {
mostrarToast('Error subiendo el archivo: ' + (upD.error || 'desconocido'), 'danger');
return;
}
// Actualizar o crear la orden médica
const orId = document.getElementById('dom-orden-id-form').value;
const orPayload = orId
? { id: parseInt(orId), local_file: upD.local_file }
: { paciente_id: parseInt(pacienteId), local_file: upD.local_file };
const orR = await fetch('api/lab/save_orden.php', {
method:'POST', headers:{'Content-Type':'application/json'},
body: JSON.stringify(orPayload),
});
const orD = await orR.json();
if (!orD.success) {
mostrarToast('Error guardando la orden: ' + (orD.error || 'desconocido'), 'danger');
return;
}
datos.orden_id = orD.id ?? orD.data?.id ?? (parseInt(orId) || null);
// Desvincular archivos que el usuario eliminó
for (const ordenId of _domRemovedOrdenIds) {
try {
await fetch('api/lab/save_orden.php', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ id: ordenId, domicilio_id: null }),
});
} catch (_e) { /* silencioso */ }
}
// ── Guardar domicilio ────────────────────────────────────────────────
if (!datos.orden_id) delete datos.orden_id;
_domRemovedOrdenIds = [];
// Guardar domicilio
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) {
modal.hide();
mostrarToast(d.message, 'success');
const editadoId = datos.id ? parseInt(datos.id) : (d.id || null);
_domResetOrden();
await cargarLista(paginaActual);
if (editadoId) verDomicilio(editadoId);
} else {
if (!d.success) {
mostrarToast(d.error||'Error', 'danger');
return;
}
const domicilioId = datos.id ? parseInt(datos.id) : (d.id || null);
// Subir nuevos archivos vinculados al domicilio
if (_domNewFiles.length && domicilioId) {
const fd = new FormData();
for (const f of _domNewFiles) fd.append('files[]', f);
fd.append('domicilio_id', domicilioId);
fd.append('paciente_id', pacienteId);
try {
const upR = await fetch('api/lab/upload_orden.php', { method: 'POST', body: fd });
const upD = await upR.json();
if (!upD.success) mostrarToast('No se pudieron subir algunos archivos: ' + (upD.error || ''), 'warning');
} catch (_e) { mostrarToast('Error al subir archivos', 'warning'); }
}
_domNewFiles = [];
modal.hide();
mostrarToast(d.message, 'success');
_domResetOrdenes();
await cargarLista(paginaActual);
if (domicilioId) verDomicilio(domicilioId);
}
// Autocompletado de paciente