From a3e9d4c2a82c327b554745700d5969ce68f5398d Mon Sep 17 00:00:00 2001 From: Nachou-u Date: Wed, 9 Sep 2026 14:58:43 -0300 Subject: [PATCH] FIX: Simplificar input de archivos para asegurar subida cross-browser --- templates/expenses/expense_form.html | 206 ++------------------------- 1 file changed, 13 insertions(+), 193 deletions(-) diff --git a/templates/expenses/expense_form.html b/templates/expenses/expense_form.html index 3a5937e..50ea475 100644 --- a/templates/expenses/expense_form.html +++ b/templates/expenses/expense_form.html @@ -129,22 +129,22 @@
- -
- -

Arrastre sus boletas/facturas aquí o haga clic para buscar

- Formatos aceptados: PDF, PNG, JPG, JPEG (Múltiple) -
- - -
- {{ form.receipt }} -
- -
+ +
+ +
+ {{ form.receipt }} +
+ Formatos aceptados: PDF, PNG, JPG, JPEG. Puede seleccionar múltiples archivos manteniendo presionada la tecla Ctrl (o Cmd en Mac). +
+
+
{% if form.receipt.errors %}
{{ form.receipt.errors }}
{% endif %} + {% if form.instance.pk and form.instance.documents.all %}
@@ -612,186 +612,6 @@ }); } - // Client-side Upload File Deletion Queue using DataTransfer + Drag and Drop - const fileInput = document.getElementById('id_receipt'); - const newFilesList = document.getElementById('new-files-list'); - const dropZone = document.getElementById('drop-zone'); - - if (fileInput && newFilesList && dropZone) { - let dt = new DataTransfer(); - - // Clicking the drop zone triggers the hidden file input - dropZone.addEventListener('click', () => { - fileInput.click(); - }); - - // Highlight drop zone on drag hover - ['dragenter', 'dragover'].forEach(eventName => { - dropZone.addEventListener(eventName, (e) => { - e.preventDefault(); - e.stopPropagation(); - dropZone.style.border = '2px dashed var(--efintel-blue)'; - dropZone.style.background = 'var(--efintel-light)'; - const icon = dropZone.querySelector('i'); - if (icon) icon.style.transform = 'scale(1.2)'; - }, false); - }); - - ['dragleave', 'drop'].forEach(eventName => { - dropZone.addEventListener(eventName, (e) => { - e.preventDefault(); - e.stopPropagation(); - dropZone.style.border = '2px dashed var(--efintel-blue)'; - dropZone.style.background = 'white'; - const icon = dropZone.querySelector('i'); - if (icon) icon.style.transform = 'scale(1)'; - }, false); - }); - - // Handle dropped files - dropZone.addEventListener('drop', (e) => { - const files = e.dataTransfer.files; - if (files.length > 0) { - for (let i = 0; i < files.length; i++) { - const f = files[i]; - // Validate file type before putting in queue (extensions: pdf, png, jpg, jpeg) - const ext = f.name.split('.').pop().toLowerCase(); - if (['pdf', 'png', 'jpg', 'jpeg'].includes(ext)) { - dt.items.add(f); - } else { - Swal.fire({ - icon: 'error', - title: 'Archivo no permitido', - text: `El archivo "${f.name}" no es una evidencia válida (PDF, PNG, JPG, JPEG).`, - confirmButtonColor: 'var(--efintel-blue)' - }); - } - } - fileInput.files = dt.files; - renderNewFiles(); - } - }); - - // Handle standard file selection - fileInput.addEventListener('change', () => { - let hasError = false; - for (let i = 0; i < fileInput.files.length; i++) { - const f = fileInput.files[i]; - const ext = f.name.split('.').pop().toLowerCase(); - if (['pdf', 'png', 'jpg', 'jpeg'].includes(ext)) { - dt.items.add(f); - } else { - hasError = true; - Swal.fire({ - icon: 'error', - title: 'Archivo no permitido', - text: `El archivo "${f.name}" no es una evidencia válida (PDF, PNG, JPG, JPEG).`, - confirmButtonColor: 'var(--efintel-blue)' - }); - } - } - fileInput.files = dt.files; - renderNewFiles(); - - // Si hubo error, resetear el input nativo temporalmente para evitar que se envíe el archivo inválido - if (hasError && dt.files.length === 0) { - fileInput.value = ''; - } - }); - - function renderNewFiles() { - newFilesList.innerHTML = ''; - - if (dt.files.length > 0) { - const header = document.createElement('strong'); - header.style.fontSize = '0.75rem'; - header.style.color = 'var(--efintel-blue)'; - header.style.display = 'block'; - header.style.marginBottom = '0.35rem'; - header.textContent = 'Archivos en cola para subir:'; - newFilesList.appendChild(header); - } - - Array.from(dt.files).forEach((file, index) => { - const row = document.createElement('div'); - row.style.display = 'flex'; - row.style.justifyContent = 'space-between'; - row.style.alignItems = 'center'; - row.style.background = 'var(--efintel-light)'; - row.style.padding = '0.45rem 0.75rem'; - row.style.borderRadius = '4px'; - row.style.fontSize = '0.75rem'; - row.style.border = '1px solid var(--efintel-border)'; - row.style.marginBottom = '0.3rem'; - - // File Detail Info with preview - const infoWrapper = document.createElement('div'); - infoWrapper.style.display = 'flex'; - infoWrapper.style.alignItems = 'center'; - infoWrapper.style.gap = '0.5rem'; - - // Check file extension - const ext = file.name.split('.').pop().toLowerCase(); - if (['png', 'jpg', 'jpeg'].includes(ext)) { - // Render image thumbnail - const img = document.createElement('img'); - img.src = URL.createObjectURL(file); - img.style.width = '24px'; - img.style.height = '24px'; - img.style.objectFit = 'cover'; - img.style.borderRadius = '3px'; - img.style.border = '1px solid #cbd5e1'; - img.onload = () => URL.revokeObjectURL(img.src); // Free memory - infoWrapper.appendChild(img); - } else if (ext === 'pdf') { - // Render PDF file icon - const pdfIcon = document.createElement('span'); - pdfIcon.innerHTML = ``; - infoWrapper.appendChild(pdfIcon); - } else { - const defaultIcon = document.createElement('span'); - defaultIcon.innerHTML = ``; - infoWrapper.appendChild(defaultIcon); - } - - const nameSpan = document.createElement('span'); - nameSpan.style.color = 'var(--efintel-petroleum)'; - nameSpan.style.fontWeight = '600'; - nameSpan.textContent = `${file.name} (${(file.size / 1024).toFixed(1)} KB)`; - infoWrapper.appendChild(nameSpan); - row.appendChild(infoWrapper); - - const deleteBtn = document.createElement('button'); - deleteBtn.type = 'button'; - deleteBtn.style.background = 'none'; - deleteBtn.style.border = 'none'; - deleteBtn.style.color = 'var(--efintel-danger)'; - deleteBtn.style.cursor = 'pointer'; - deleteBtn.style.padding = '0.2rem'; - deleteBtn.style.display = 'flex'; - deleteBtn.style.alignItems = 'center'; - deleteBtn.innerHTML = ``; - - deleteBtn.addEventListener('click', (e) => { - e.preventDefault(); - const newDt = new DataTransfer(); - for (let i = 0; i < dt.files.length; i++) { - if (i !== index) { - newDt.items.add(dt.files[i]); - } - } - dt = newDt; - fileInput.files = dt.files; - renderNewFiles(); - }); - row.appendChild(deleteBtn); - - newFilesList.appendChild(row); - }); - } - } - - // Toggle logic for unregistered vendor inputs const toggleNewVendor = document.getElementById('toggle-new-vendor'); const newVendorFields = document.getElementById('new-vendor-fields'); const originalVendorSelect = document.getElementById('id_vendor');