Spaces:
Running
Running
| document.addEventListener('DOMContentLoaded', function() { | |
| const templateUpload = document.getElementById('templateUpload'); | |
| const uploadTemplateBtn = document.getElementById('uploadTemplateBtn'); | |
| const scanPdfBtn = document.getElementById('scanPdfBtn'); | |
| const previewPdfBtn = document.getElementById('previewPdfBtn'); | |
| const csvPreview = document.getElementById('csvPreview'); | |
| const rowSelection = document.getElementById('rowSelection'); | |
| const generatePdfBtn = document.getElementById('generatePdfBtn'); | |
| const downloadSection = document.getElementById('downloadSection'); | |
| const downloadLink = document.getElementById('downloadLink'); | |
| let csvData = []; | |
| let headers = []; | |
| let pdfDoc = null; | |
| let pdfFields = []; | |
| // Handle template PDF locally | |
| uploadTemplateBtn.addEventListener('click', async () => { | |
| let file; | |
| const localPathInput = document.getElementById('localPath'); | |
| if (localPathInput.value) { | |
| try { | |
| // Handle local path input | |
| if (!localPathInput.value.startsWith('http') && !localPathInput.value.startsWith('file://')) { | |
| alert('Local paths must start with "file://" (e.g., file:///C:/path/to/file.pdf)'); | |
| return; | |
| } | |
| const response = await fetch(localPathInput.value, { | |
| mode: 'cors', | |
| headers: { | |
| 'Content-Type': 'application/pdf' | |
| } | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`HTTP error! status: ${response.status}`); | |
| } | |
| const blob = await response.blob(); | |
| if (blob.size === 0) { | |
| throw new Error('Empty file received'); | |
| } | |
| file = new File([blob], localPathInput.value.split('/').pop() || 'document.pdf', { type: 'application/pdf' }); | |
| } catch (error) { | |
| console.error('File loading error:', error); | |
| alert(`Error loading file: ${error.message}\n\nMake sure:\n1. Path starts with file://\n2. File exists\n3. CORS is enabled for local files`); | |
| return; | |
| } | |
| } else if (templateUpload.files.length) { | |
| file = templateUpload.files[0]; | |
| } else { | |
| alert('Please select a PDF file or enter a local path first'); | |
| return; | |
| } | |
| const today = new Date(); | |
| const dateString = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; | |
| try { | |
| // Initialize PDF.js | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js'; | |
| const fileHandle = await todayDirHandle.getFileHandle(file.name, { create: true }); | |
| const writable = await fileHandle.createWritable(); | |
| await writable.write(file); | |
| await writable.close(); | |
| }); | |
| // Display PDF preview | |
| const pdfViewer = document.getElementById('pdfViewer'); | |
| pdfViewer.src = URL.createObjectURL(file); | |
| // Load PDF and extract form fields | |
| const fileReader = new FileReader(); | |
| fileReader.onload = async function() { | |
| const typedArray = new Uint8Array(this.result); | |
| pdfDoc = await pdfjsLib.getDocument(typedArray).promise; | |
| // Get form fields (this is a simplified approach) | |
| const page = await pdfDoc.getPage(1); | |
| const textContent = await page.getTextContent(); | |
| const textItems = textContent.items.map(item => item.str); | |
| // Filter out empty strings and common PDF text | |
| pdfFields = textItems.filter(text => | |
| text.trim().length > 0 && | |
| !text.match(/page|©|http|www|\.com|\.org|\.net/i) | |
| ); | |
| // Display found fields | |
| const fieldsList = document.getElementById('invoiceFieldsList'); | |
| fieldsList.innerHTML = ''; | |
| pdfFields.forEach(field => { | |
| const li = document.createElement('li'); | |
| li.className = 'list-group-item'; | |
| li.textContent = field; | |
| fieldsList.appendChild(li); | |
| }); | |
| // Show the fields section | |
| document.getElementById('pdfPreview').classList.remove('d-none'); | |
| }; | |
| fileReader.readAsArrayBuffer(file); | |
| } catch (error) { | |
| console.error('Error uploading template:', error); | |
| alert('Failed to upload template'); | |
| } | |
| }); | |
| // Add new field functionality | |
| document.getElementById('addFieldBtn').addEventListener('click', () => { | |
| const nameInput = document.getElementById('newFieldName'); | |
| const valueInput = document.getElementById('newFieldValue'); | |
| const fieldsList = document.getElementById('newFieldsList'); | |
| if (!nameInput.value.trim()) { | |
| alert('Please enter a field name'); | |
| return; | |
| } | |
| const fieldItem = document.createElement('div'); | |
| fieldItem.className = 'list-group-item d-flex justify-content-between align-items-center'; | |
| fieldItem.innerHTML = ` | |
| <span><strong>${nameInput.value}:</strong> ${valueInput.value}</span> | |
| <button class="btn btn-sm btn-danger remove-field">×</button> | |
| `; | |
| fieldsList.appendChild(fieldItem); | |
| // Clear inputs | |
| nameInput.value = ''; | |
| valueInput.value = ''; | |
| // Add remove functionality | |
| fieldItem.querySelector('.remove-field').addEventListener('click', () => { | |
| fieldItem.remove(); | |
| }); | |
| }); | |
| // Handle PDF scanning | |
| scanPdfBtn.addEventListener('click', async () => { | |
| if (!pdfDoc) { | |
| alert('Please upload and process a PDF file first'); | |
| return; | |
| } | |
| // Extract common invoice fields (simplified example) | |
| const invoiceNumber = pdfFields.find(field => | |
| field.match(/invoice|מספר|חשבונית|מספר חשבונית/i) | |
| ); | |
| const dateField = pdfFields.find(field => | |
| field.match(/date|תאריך|date issued/i) | |
| ); | |
| const reasonField = pdfFields.find(field => | |
| field.match(/treatment|reason|service|סוג|שירות/i) | |
| ); | |
| // Update UI with found fields | |
| document.getElementById('invoiceNumber').textContent = | |
| invoiceNumber || 'Not found in PDF'; | |
| document.getElementById('invoiceDate').textContent = | |
| dateField || 'Not found in PDF'; | |
| document.getElementById('treatmentReason').textContent = | |
| reasonField || 'Not found in PDF'; | |
| }); | |
| // Initialize PDF.js | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.12.313/pdf.worker.min.js'; | |
| // Handle PDF preview | |
| previewPdfBtn.addEventListener('click', async () => { | |
| if (!templateUpload.files.length) { | |
| alert('Please upload a PDF file first'); | |
| return; | |
| } | |
| const file = templateUpload.files[0]; | |
| const pdfViewer = document.getElementById('pdfViewer'); | |
| const fileReader = new FileReader(); | |
| fileReader.onload = async function() { | |
| const typedArray = new Uint8Array(this.result); | |
| try { | |
| // Load the PDF document | |
| const pdf = await pdfjsLib.getDocument(typedArray).promise; | |
| // Get the first page | |
| const page = await pdf.getPage(1); | |
| // Set the scale and viewport | |
| const scale = 1.5; | |
| const viewport = page.getViewport({ scale }); | |
| // Create canvas for rendering | |
| const canvas = document.createElement('canvas'); | |
| const context = canvas.getContext('2d'); | |
| canvas.height = viewport.height; | |
| canvas.width = viewport.width; | |
| // Clear previous content | |
| pdfViewer.innerHTML = ''; | |
| pdfViewer.appendChild(canvas); | |
| pdfViewer.style.height = '600px'; | |
| // Render PDF page | |
| await page.render({ | |
| canvasContext: context, | |
| viewport: viewport | |
| }).promise; | |
| } catch (error) { | |
| console.error('Error rendering PDF:', error); | |
| alert('Error loading PDF. Please try another file.'); | |
| } | |
| }; | |
| fileReader.readAsArrayBuffer(file); | |
| }); | |
| // Handle CSV upload | |
| const csvUpload = document.getElementById('csvUpload'); | |
| csvUpload?.addEventListener('change', function(e) { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| const content = e.target.result; | |
| const lines = content.split('\n'); | |
| if (lines.length === 0) { | |
| alert('CSV file is empty'); | |
| return; | |
| } | |
| // Parse CSV | |
| headers = lines[0].split(','); | |
| csvData = lines.slice(1).map(line => line.split(',')); | |
| // Display headers | |
| const thead = csvPreview.querySelector('thead'); | |
| thead.innerHTML = ''; | |
| const headerRow = document.createElement('tr'); | |
| headers.forEach(header => { | |
| const th = document.createElement('th'); | |
| th.textContent = header; | |
| headerRow.appendChild(th); | |
| }); | |
| thead.appendChild(headerRow); | |
| // Display first 5 rows | |
| const tbody = csvPreview.querySelector('tbody'); | |
| tbody.innerHTML = ''; | |
| csvData.slice(0, 5).forEach(row => { | |
| const tr = document.createElement('tr'); | |
| row.forEach(cell => { | |
| const td = document.createElement('td'); | |
| td.textContent = cell; | |
| tr.appendChild(td); | |
| }); | |
| tbody.appendChild(tr); | |
| }); | |
| // Populate row selection | |
| rowSelection.innerHTML = ''; | |
| csvData.forEach((_, index) => { | |
| const option = document.createElement('option'); | |
| option.value = index; | |
| option.textContent = `Row ${index + 1}`; | |
| rowSelection.appendChild(option); | |
| }); | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| // Generate PDFs locally | |
| generatePdfBtn.addEventListener('click', async () => { | |
| if (csvData.length === 0) { | |
| alert('Please upload and process CSV data first'); | |
| return; | |
| } | |
| const selectedRows = Array.from(rowSelection.selectedOptions).map(opt => parseInt(opt.value)); | |
| if (selectedRows.length === 0) { | |
| alert('Please select at least one row to process'); | |
| return; | |
| } | |
| try { | |
| // Create a zip file with all generated PDFs | |
| const zip = new JSZip(); | |
| const today = new Date(); | |
| const dateString = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`; | |
| // Generate a PDF for each selected row | |
| for (const rowIndex of selectedRows) { | |
| const rowData = csvData[rowIndex]; | |
| const fileName = `invoice_${rowIndex + 1}.pdf`; | |
| // Create a simple PDF (in a real app, use a proper PDF generation library) | |
| const pdfContent = ` | |
| Invoice Details | |
| -------------- | |
| Invoice Number: ${rowData[0] || 'N/A'} | |
| Date: ${rowData[1] || 'N/A'} | |
| Treatment: ${rowData[2] || 'N/A'} | |
| Amount: ${rowData[3] || 'N/A'} | |
| `; | |
| zip.file(fileName, pdfContent); | |
| } | |
| // Generate the zip file | |
| const zipContent = await zip.generateAsync({type: 'blob'}); | |
| const zipUrl = URL.createObjectURL(zipContent); | |
| // Update download link | |
| downloadLink.href = zipUrl; | |
| downloadLink.textContent = selectedRows.length > 1 ? | |
| `Download ${selectedRows.length} PDFs (ZIP)` : | |
| `Download PDF`; | |
| downloadLink.setAttribute('download', `invoices_${dateString}.zip`); | |
| downloadSection.classList.remove('d-none'); | |
| } catch (error) { | |
| console.error('Error generating PDFs:', error); | |
| alert('Failed to generate PDFs'); | |
| } | |
| }); | |
| }); |