Download script.js from alcantarm/intento2: direct link, hf CLI and curl.
- Browser
- Download file 7.67 kB
-
https://huggingface.co/spaces/alcantarm/intento2/resolve/main/script.js
- Command line
-
hf download hf://spaces/alcantarm/intento2/script.js
-
curl -L -o script.js https://huggingface.co/spaces/alcantarm/intento2/resolve/main/script.js
7.67 kB
| window.addEventListener('DOMContentLoaded', () => { | |
| const CLP = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', maximumFractionDigits: 0 }); | |
| // Inputs y elementos | |
| const input = document.getElementById('invoice-input'); | |
| const budgetInput = document.getElementById('budget-input'); | |
| const dropzone = document.getElementById('dropzone'); | |
| const budgetDropzone = document.getElementById('budget-dropzone'); | |
| const tbody = document.getElementById('tbody'); | |
| const budgetTbody = document.getElementById('budget-tbody-right'); | |
| const total = document.getElementById('total'); | |
| const errorEl = document.getElementById('error'); | |
| const budgetError = document.getElementById('budget-error'); | |
| const processBtn = document.getElementById('process-btn'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| const summary = document.getElementById('summary'); | |
| const recordsTbody = document.getElementById('records-tbody'); | |
| // Datos | |
| const docs = []; | |
| const records = []; | |
| let budgetFile = null; | |
| // Funciones auxiliares | |
| const fmtDate = iso => new Date(iso).toLocaleString(); | |
| const randomAmount = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; | |
| const randomFolio = () => 'F' + Math.floor(100000 + Math.random() * 900000); | |
| const parseCSV = content => { | |
| const lines = content.split('\n'); | |
| for (const line of lines) { | |
| const matches = line.match(/(\d{1,3}(?:\.\d{3})*|\d+)/g); | |
| if (matches && matches.length > 0) { | |
| const amount = parseInt(matches[0].replace(/\./g, '')); | |
| if (!isNaN(amount) && amount > 1000) return amount; | |
| } | |
| } | |
| return null; | |
| }; | |
| // Renderizar tablas | |
| const renderDocs = () => { | |
| total.textContent = `${docs.length} en total`; | |
| tbody.innerHTML = ''; | |
| if (!docs.length) { | |
| tbody.innerHTML = `<tr><td colspan="5" style="text-align:center;color:var(--muted);padding:24px;">Aún no hay documentos. Sube un PDF o imagen para comenzar.</td></tr>`; | |
| } else { | |
| docs.forEach((d, i) => { | |
| const monto = CLP.format(d.amount); | |
| const tr = document.createElement('tr'); | |
| tr.innerHTML = `<td>${i+1}</td><td>${d.folio}</td><td>${d.name}</td><td>${fmtDate(d.loadedAt)}</td><td>${monto}</td>`; | |
| tbody.appendChild(tr); | |
| }); | |
| } | |
| }; | |
| const renderBudget = () => { | |
| budgetTbody.innerHTML = ''; | |
| if (!budgetFile) { | |
| budgetTbody.innerHTML = `<tr><td colspan="3" style="text-align:center;color:var(--muted);padding:16px;">Aún no se ha cargado el presupuesto.</td></tr>`; | |
| } else { | |
| const tr = document.createElement('tr'); | |
| tr.innerHTML = `<td>${budgetFile.name}</td><td>${fmtDate(budgetFile.loadedAt)}</td><td>${CLP.format(budgetFile.amount)}</td>`; | |
| budgetTbody.appendChild(tr); | |
| } | |
| }; | |
| const updateSummary = () => { | |
| if (!budgetFile || !docs.length) { | |
| summary.innerHTML = ''; | |
| return; | |
| } | |
| const totalDocs = docs.reduce((sum, doc) => sum + (doc.amount || 0), 0); | |
| const variation = budgetFile.amount - totalDocs; | |
| const percentVariation = budgetFile.amount > 0 ? (variation / budgetFile.amount * 100) : 0; | |
| summary.innerHTML = `<strong>Total documentos:</strong> ${CLP.format(totalDocs)}<br> | |
| <strong>Presupuesto:</strong> ${CLP.format(budgetFile.amount)}<br> | |
| <strong>Variación:</strong> ${CLP.format(variation)} (${percentVariation.toFixed(1)}%)`; | |
| }; | |
| const renderRecords = () => { | |
| recordsTbody.innerHTML = ''; | |
| if (!records.length) { | |
| recordsTbody.innerHTML = `<tr><td class="empty" colspan="5">No hay registros todavía. Guarda un proceso para verlo aquí.</td></tr>`; | |
| } else { | |
| records.forEach((record, i) => { | |
| const tr = document.createElement('tr'); | |
| const variationText = record.diff >= 0 ? `+${CLP.format(record.diff)}` : CLP.format(record.diff); | |
| tr.innerHTML = `<td>${record.docsCount}</td><td>${CLP.format(record.docsSum)}</td><td>${CLP.format(record.budget)}</td><td>${variationText}</td> | |
| <td><button class="delete-btn" onclick="deleteRecord(${i})">Eliminar</button></td>`; | |
| recordsTbody.appendChild(tr); | |
| }); | |
| } | |
| }; | |
| window.deleteRecord = idx => { | |
| if (!isNaN(idx) && idx >= 0 && idx < records.length) { | |
| records.splice(idx, 1); | |
| renderRecords(); | |
| } | |
| }; | |
| // Subir archivos | |
| const addFiles = files => { | |
| const nowIso = new Date().toISOString(); | |
| errorEl.textContent = ''; | |
| Array.from(files).forEach(f => { | |
| if (f) { | |
| docs.unshift({ name: f.name, loadedAt: nowIso, file: f, amount: randomAmount(10000, 250000), folio: randomFolio() }); | |
| } else { | |
| errorEl.textContent = 'Solo se permiten archivos PDF o imágenes.'; | |
| } | |
| }); | |
| renderDocs(); | |
| updateSummary(); | |
| }; | |
| const setBudgetFromFile = f => { | |
| if (!f) return; | |
| if (!f.name.toLowerCase().endsWith('.csv')) { | |
| budgetError.textContent = 'Solo se permite un archivo CSV.'; | |
| return; | |
| } | |
| budgetError.textContent = ''; | |
| const reader = new FileReader(); | |
| reader.onload = e => { | |
| const content = e.target.result; | |
| let amount = parseCSV(content); | |
| if (amount === null) amount = randomAmount(200000, 2000000); | |
| budgetFile = { name: f.name, loadedAt: new Date().toISOString(), amount }; | |
| renderBudget(); | |
| updateSummary(); | |
| }; | |
| reader.readAsText(f); | |
| }; | |
| // Eventos | |
| input.addEventListener('change', () => { addFiles(input.files); input.value = ''; }); | |
| budgetInput.addEventListener('change', () => { if (budgetInput.files.length > 0) { setBudgetFromFile(budgetInput.files[0]); budgetInput.value = ''; } }); | |
| // Drag & drop facturas | |
| ['dragenter', 'dragover'].forEach(evt => dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('drag'); })); | |
| ['dragleave', 'drop'].forEach(evt => dropzone.addEventListener(evt, e => { | |
| e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('drag'); | |
| if (e.type === 'drop') addFiles(e.dataTransfer.files); | |
| })); | |
| // Drag & drop presupuesto | |
| ['dragenter', 'dragover'].forEach(evt => budgetDropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); budgetDropzone.classList.add('drag'); })); | |
| ['dragleave', 'drop'].forEach(evt => budgetDropzone.addEventListener(evt, e => { | |
| e.preventDefault(); e.stopPropagation(); budgetDropzone.classList.remove('drag'); | |
| if (e.type === 'drop' && e.dataTransfer.files.length > 0) setBudgetFromFile(e.dataTransfer.files[0]); | |
| })); | |
| processBtn.addEventListener('click', async () => { | |
| if (!budgetFile || !docs.length) { | |
| alert('Carga al menos un PDF/imagen y un CSV de presupuesto.'); | |
| return; | |
| } | |
| processBtn.textContent = 'Procesando...'; processBtn.disabled = true; | |
| await new Promise(r => setTimeout(r, 800)); | |
| const totalDocs = docs.reduce((sum, d) => sum + d.amount, 0); | |
| records.push({ docsCount: docs.length, docsSum: totalDocs, budget: budgetFile.amount, diff: budgetFile.amount - totalDocs }); | |
| renderRecords(); | |
| processBtn.textContent = 'Procesar'; processBtn.disabled = false; | |
| }); | |
| resetBtn.addEventListener('click', () => { | |
| if (confirm('¿Estás seguro de que quieres reiniciar todo?')) { | |
| docs.length = 0; | |
| records.length = 0; | |
| budgetFile = null; | |
| errorEl.textContent = ''; | |
| budgetError.textContent = ''; | |
| input.value = ''; | |
| budgetInput.value = ''; | |
| renderDocs(); | |
| renderBudget(); | |
| renderRecords(); | |
| updateSummary(); | |
| summary.innerHTML = ''; | |
| } | |
| }); | |
| // Inicial | |
| renderDocs(); | |
| renderBudget(); | |
| renderRecords(); | |
| }); | |