multiples documentos

This commit is contained in:
Lizandro Guarnizo
2026-04-08 09:58:08 -05:00
parent db254ca02f
commit 882ce56e96
4 changed files with 216 additions and 70 deletions
+100 -35
View File
@@ -1931,9 +1931,7 @@ const notasManager = {
</div>
</div>
<div class="nota-cuerpo">${n.cuerpo ? n.cuerpo.replace(/\n/g,'<br>') : ''}</div>
${n.imagen_path ? `<img src="uploads/media/${esc(n.imagen_path)}"
class="img-fluid rounded mt-1" style="max-height:160px;cursor:pointer"
onclick="notasManager._verImg(this.src)">` : ''}
${_renderArchivosNota(n)}
<div class="nota-fecha">${esc(n.created_at||'')}</div>
</div>`;
});
@@ -1948,9 +1946,10 @@ const notasManager = {
// ── Bottom sheet: Ficha clínica ────────────────────────────────────────
abrirFicha(domId) {
this._domId = domId;
this._notaId = 'ficha';
this._imagen = null;
this._domId = domId;
this._notaId = 'ficha';
this._imagen = null;
this._archivos = [];
const body = document.getElementById(`notas-body-${domId}`);
const pac = body ? body.dataset.pacNac || '' : '';
@@ -1992,9 +1991,10 @@ const notasManager = {
// ── Bottom sheet: Nota libre ───────────────────────────────────────────
abrirLibre(domId, notaId) {
this._domId = domId;
this._notaId = notaId;
this._imagen = null;
this._domId = domId;
this._notaId = notaId;
this._imagen = null;
this._archivos = [];
let titulo = '', cuerpo = '';
if (notaId) {
@@ -2023,20 +2023,25 @@ const notasManager = {
data-placeholder="Escribe aquí la observación...">${cuerpo}</div>
</div>
<div class="nota-section">
<label class="nota-label"><i class="fas fa-image me-1"></i>Imagen (opcional)</label>
<div class="d-flex gap-2">
<label class="nota-label"><i class="fas fa-paperclip me-1"></i>Archivos adjuntos (opcional)</label>
<div class="d-flex gap-2 flex-wrap">
<label class="btn btn-sm btn-outline-secondary" style="font-size:.76rem">
<i class="fas fa-camera me-1"></i>Cámara
<input type="file" accept="image/*" capture="environment" class="d-none"
onchange="notasManager._prevImagen(this)">
<input type="file" accept="image/*" capture="environment" class="d-none" multiple
onchange="notasManager._agregarArchivos(this)">
</label>
<label class="btn btn-sm btn-outline-secondary" style="font-size:.76rem">
<i class="fas fa-image me-1"></i>Galería
<input type="file" accept="image/*" class="d-none"
onchange="notasManager._prevImagen(this)">
<input type="file" accept="image/*" class="d-none" multiple
onchange="notasManager._agregarArchivos(this)">
</label>
<label class="btn btn-sm btn-outline-secondary" style="font-size:.76rem">
<i class="fas fa-file me-1"></i>Archivos
<input type="file" accept=".pdf,.doc,.docx,.xls,.xlsx,.txt,.csv,image/*" class="d-none" multiple
onchange="notasManager._agregarArchivos(this)">
</label>
</div>
<div id="ns-img-preview" class="mt-2"></div>
<div id="ns-archivos-preview" class="mt-2 d-flex flex-wrap gap-2"></div>
</div>
<button class="nota-btn-guardar" style="background:#059669"
onclick="notasManager._guardarLibre()">
@@ -2092,14 +2097,15 @@ const notasManager = {
const cuerpo = document.getElementById('ns-cuerpo')?.innerHTML || '';
if (!titulo) { alert('Escribe un título para la nota.'); return; }
let imagenPath = null;
if (this._imagen) {
// Subir archivos pendientes
let archivos = [];
if (this._archivos && this._archivos.length) {
const fd = new FormData();
fd.append('file', this._imagen);
this._archivos.forEach(f => fd.append('files[]', f));
const ru = await fetch('api/lab/upload_nota_imagen.php', { method:'POST', body: fd });
const du = await ru.json();
if (!du.success) { alert('No se pudo subir la imagen: ' + (du.error||'')); return; }
imagenPath = du.imagen_path;
if (!du.success) { alert('No se pudo subir uno o más archivos: ' + (du.error||'')); return; }
archivos = du.archivos || [];
}
try {
@@ -2108,7 +2114,7 @@ const notasManager = {
domicilio_id: this._domId,
titulo,
cuerpo,
imagen_path: imagenPath,
archivos,
};
if (this._notaId) payload.id = this._notaId;
@@ -2159,24 +2165,51 @@ const notasManager = {
document.getElementById('nota-backdrop').style.display = 'none';
document.getElementById('nota-sheet').style.display = 'none';
document.body.style.overflow = '';
this._domId = null; this._notaId = null; this._imagen = null;
this._domId = null; this._notaId = null; this._imagen = null; this._archivos = [];
},
_fmt(cmd) { document.execCommand(cmd, false, null); document.getElementById('ns-cuerpo')?.focus(); },
_toggleAcudiente(on) {
document.getElementById('ns-acudiente-bloque')?.classList.toggle('d-none', !on);
},
_prevImagen(input) {
const file = input.files?.[0];
if (!file) return;
this._imagen = file;
const reader = new FileReader();
reader.onload = e => {
const prev = document.getElementById('ns-img-preview');
if (prev) prev.innerHTML = `<img src="${e.target.result}"
class="img-fluid rounded" style="max-height:140px">
<div class="small text-muted mt-1">${esc(file.name)}</div>`;
};
reader.readAsDataURL(file);
_agregarArchivos(input) {
if (!input.files?.length) return;
if (!this._archivos) this._archivos = [];
const prev = document.getElementById('ns-archivos-preview');
Array.from(input.files).forEach(file => {
const idx = this._archivos.length;
this._archivos.push(file);
const esImg = file.type.startsWith('image/');
const chip = document.createElement('div');
chip.className = 'position-relative';
chip.id = `arch-chip-${idx}`;
if (esImg) {
const reader = new FileReader();
reader.onload = e => {
chip.innerHTML = `
<img src="${e.target.result}" class="rounded" style="height:70px;width:70px;object-fit:cover">
<button onclick="notasManager._quitarArchivo(${idx})" class="btn btn-danger btn-xs py-0 px-1 position-absolute" style="top:-6px;right:-6px;font-size:.65rem;border-radius:50%"><i class="fas fa-times"></i></button>`;
};
reader.readAsDataURL(file);
} else {
const icon = file.type === 'application/pdf' ? 'fa-file-pdf text-danger'
: file.type.includes('word') ? 'fa-file-word text-primary'
: file.type.includes('excel') || file.type.includes('sheet') ? 'fa-file-excel text-success'
: 'fa-file text-secondary';
chip.innerHTML = `
<div class="border rounded p-1 text-center" style="width:70px;height:70px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px">
<i class="fas ${icon} fa-lg"></i>
<div style="font-size:.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:65px">${esc(file.name)}</div>
</div>
<button onclick="notasManager._quitarArchivo(${idx})" class="btn btn-danger btn-xs py-0 px-1 position-absolute" style="top:-6px;right:-6px;font-size:.65rem;border-radius:50%"><i class="fas fa-times"></i></button>`;
}
if (prev) prev.appendChild(chip);
});
input.value = ''; // reset para poder volver a elegir el mismo archivo
},
_quitarArchivo(idx) {
if (this._archivos) this._archivos[idx] = null;
const chip = document.getElementById(`arch-chip-${idx}`);
if (chip) chip.remove();
},
_verImg(src) {
const w = window.open('', '_blank');
@@ -2191,6 +2224,38 @@ const notasManager = {
return años < 18;
},
};
// Render de archivos adjuntos guardados en una nota
function _renderArchivosNota(n) {
// Nuevo: array archivos JSON
let lista = [];
if (n.archivos) {
try { lista = typeof n.archivos === 'string' ? JSON.parse(n.archivos) : n.archivos; } catch(e) {}
}
// Retrocompatibilidad: imagen_path antigua
if (!lista.length && n.imagen_path) {
lista = [{ nombre: n.imagen_path, path: n.imagen_path, mime: 'image/jpeg', size: 0 }];
}
if (!lista.length) return '';
return '<div class="d-flex flex-wrap gap-2 mt-2">' + lista.map(a => {
const src = `uploads/media/${esc(a.path)}`;
const esImg = (a.mime || '').startsWith('image/');
if (esImg) {
return `<img src="${src}" class="rounded" style="height:70px;width:70px;object-fit:cover;cursor:pointer"
onclick="notasManager._verImg(this.src)" title="${esc(a.nombre)}">`;
}
const icon = (a.mime||'').includes('pdf') ? 'fa-file-pdf text-danger'
: (a.mime||'').includes('word') ? 'fa-file-word text-primary'
: (a.mime||'').includes('excel') || (a.mime||'').includes('sheet') ? 'fa-file-excel text-success'
: 'fa-file text-secondary';
return `<a href="${src}" target="_blank" class="border rounded p-1 text-center text-decoration-none"
style="width:70px;height:70px;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;color:inherit">
<i class="fas ${icon} fa-lg"></i>
<div style="font-size:.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:65px">${esc(a.nombre)}</div>
</a>`;
}).join('') + '</div>';
}
</script>
<!-- ── Bottom sheet: editor de notas ──────────────────────────── -->
<div id="nota-backdrop" onclick="notasManager._cerrarSheet()"></div>