Spaces:
Running
Running
File size: 13,473 Bytes
aff706e f95af99 aff706e 4e6693d 72f8dfa aff706e dbda29e a50a09a dbda29e a50a09a dbda29e a50a09a dbda29e a50a09a dbda29e aff706e dbda29e 72f8dfa aff706e a50a09a 4e6693d 72f8dfa aff706e d39bca9 72f8dfa 4e6693d aff706e d39bca9 f95af99 4e6693d aff706e f95af99 4e6693d f95af99 4e6693d 68f03a1 f95af99 68f03a1 f95af99 4e6693d aff706e 72f8dfa aff706e 4e6693d aff706e 4e6693d 72f8dfa aff706e 4e6693d 72f8dfa 4e6693d aff706e 4e6693d aff706e 72f8dfa 4e6693d 72f8dfa 4e6693d 72f8dfa 4e6693d aff706e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 | 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');
}
});
}); |