Spaces:
Running
Running
Download script.js from vickey333/invoicewizard-pro: direct link, hf CLI and curl.
- Browser
- Download file 11.4 kB
-
https://huggingface.co/spaces/vickey333/invoicewizard-pro/resolve/main/script.js
- Command line
-
hf download hf://spaces/vickey333/invoicewizard-pro/script.js
-
curl -L -o script.js https://huggingface.co/spaces/vickey333/invoicewizard-pro/resolve/main/script.js
11.4 kB
| document.addEventListener('DOMContentLoaded', function() { | |
| let itemCount = 0; | |
| const itemsTable = document.getElementById('itemsTable'); | |
| // Load saved "From" details if they exist | |
| const savedFromDetails = JSON.parse(localStorage.getItem('fromDetails')) || {}; | |
| if (savedFromDetails.businessName) { | |
| document.querySelector('input[placeholder="Your Business Name"]').value = savedFromDetails.businessName; | |
| } | |
| if (savedFromDetails.address) { | |
| document.querySelector('input[placeholder="Address"]').value = savedFromDetails.address; | |
| } | |
| if (savedFromDetails.cityStateZip) { | |
| document.querySelector('input[placeholder="City, State, ZIP"]').value = savedFromDetails.cityStateZip; | |
| } | |
| if (savedFromDetails.email) { | |
| document.querySelector('input[placeholder="Email"]').value = savedFromDetails.email; | |
| } | |
| if (savedFromDetails.phone) { | |
| document.querySelector('input[placeholder="Phone"]').value = savedFromDetails.phone; | |
| } | |
| // Save "From" details when they change | |
| const fromInputs = document.querySelectorAll('input[placeholder^="Your"]'); | |
| fromInputs.forEach(input => { | |
| input.addEventListener('change', () => { | |
| const fromDetails = { | |
| businessName: document.querySelector('input[placeholder="Your Business Name"]').value, | |
| address: document.querySelector('input[placeholder="Address"]').value, | |
| cityStateZip: document.querySelector('input[placeholder="City, State, ZIP"]').value, | |
| email: document.querySelector('input[placeholder="Email"]').value, | |
| phone: document.querySelector('input[placeholder="Phone"]').value | |
| }; | |
| localStorage.setItem('fromDetails', JSON.stringify(fromDetails)); | |
| }); | |
| }); | |
| const addItemBtn = document.getElementById('addItemBtn'); | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const closeModalBtn = document.getElementById('closeModalBtn'); | |
| const printBtn = document.getElementById('printBtn'); | |
| const downloadBtn = document.getElementById('downloadBtn'); | |
| const invoiceModal = document.getElementById('invoiceModal'); | |
| const invoiceContent = document.getElementById('invoiceContent'); | |
| const taxRateInput = document.getElementById('taxRate'); | |
| // Add new item row | |
| addItemBtn.addEventListener('click', addItemRow); | |
| // Generate invoice | |
| generateBtn.addEventListener('click', generateInvoice); | |
| // Close modal | |
| closeModalBtn.addEventListener('click', () => { | |
| invoiceModal.classList.add('hidden'); | |
| }); | |
| // Print invoice | |
| printBtn.addEventListener('click', () => { | |
| window.print(); | |
| }); | |
| // Download as PDF (placeholder functionality) | |
| downloadBtn.addEventListener('click', () => { | |
| alert('PDF download functionality would be implemented here with a library like jsPDF'); | |
| }); | |
| // Recalculate totals when tax rate changes | |
| taxRateInput.addEventListener('change', calculateTotals); | |
| // Add initial item row | |
| addItemRow(); | |
| function addItemRow() { | |
| itemCount++; | |
| const row = document.createElement('tr'); | |
| row.className = 'item-row'; | |
| row.innerHTML = ` | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <input type="text" class="w-full px-2 py-1 border rounded description" placeholder="Item description"> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <input type="number" min="1" value="1" class="w-20 px-2 py-1 border rounded quantity"> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <input type="number" min="0" step="0.01" value="0.00" class="w-24 px-2 py-1 border rounded price"> | |
| </td> | |
| <td class="px-6 py-4 whitespace-nowrap item-total font-medium">$0.00</td> | |
| <td class="px-6 py-4 whitespace-nowrap"> | |
| <button class="delete-item text-red-500 hover:text-red-700"> | |
| <i data-feather="trash-2"></i> | |
| </button> | |
| </td> | |
| `; | |
| itemsTable.appendChild(row); | |
| feather.replace(); | |
| // Add event listeners for the new row | |
| const quantityInput = row.querySelector('.quantity'); | |
| const priceInput = row.querySelector('.price'); | |
| const deleteBtn = row.querySelector('.delete-item'); | |
| quantityInput.addEventListener('input', calculateRowTotal); | |
| priceInput.addEventListener('input', calculateRowTotal); | |
| deleteBtn.addEventListener('click', () => { | |
| row.remove(); | |
| calculateTotals(); | |
| }); | |
| function calculateRowTotal() { | |
| const quantity = parseFloat(quantityInput.value) || 0; | |
| const price = parseFloat(priceInput.value) || 0; | |
| const total = quantity * price; | |
| row.querySelector('.item-total').textContent = `$${total.toFixed(2)}`; | |
| calculateTotals(); | |
| } | |
| } | |
| function calculateTotals() { | |
| const rows = document.querySelectorAll('.item-row'); | |
| let subtotal = 0; | |
| rows.forEach(row => { | |
| const totalText = row.querySelector('.item-total').textContent; | |
| subtotal += parseFloat(totalText.replace('$', '')) || 0; | |
| }); | |
| const taxRate = parseFloat(taxRateInput.value) || 0; | |
| const taxAmount = subtotal * (taxRate / 100); | |
| const total = subtotal + taxAmount; | |
| document.getElementById('subtotal').textContent = `$${subtotal.toFixed(2)}`; | |
| document.getElementById('taxAmount').textContent = `$${taxAmount.toFixed(2)}`; | |
| document.getElementById('totalAmount').textContent = `$${total.toFixed(2)}`; | |
| } | |
| function generateInvoice() { | |
| const fromName = document.querySelector('input[placeholder="Your Business Name"]').value || 'Your Business Name'; | |
| const fromAddress = document.querySelector('input[placeholder="Address"]').value || 'Your Address'; | |
| const fromCityStateZip = document.querySelector('input[placeholder="City, State, ZIP"]').value || 'City, State, ZIP'; | |
| const fromEmail = document.querySelector('input[placeholder="Email"]').value || 'your@email.com'; | |
| const fromPhone = document.querySelector('input[placeholder="Phone"]').value || '(123) 456-7890'; | |
| const toName = document.querySelector('input[placeholder="Client Name"]').value || 'Client Name'; | |
| const toAddress = document.querySelector('input[placeholder="Client Address"]').value || 'Client Address'; | |
| const toCityStateZip = document.querySelector('input[placeholder="City, State, ZIP"]').value || 'City, State, ZIP'; | |
| const toEmail = document.querySelector('input[placeholder="Client Email"]').value || 'client@email.com'; | |
| const toPhone = document.querySelector('input[placeholder="Client Phone"]').value || '(987) 654-3210'; | |
| const invoiceDate = new Date().toLocaleDateString(); | |
| const invoiceNumber = 'INV-' + Math.floor(Math.random() * 10000); | |
| const subtotal = document.getElementById('subtotal').textContent; | |
| const taxAmount = document.getElementById('taxAmount').textContent; | |
| const totalAmount = document.getElementById('totalAmount').textContent; | |
| const taxRate = taxRateInput.value; | |
| let itemsHTML = ''; | |
| const rows = document.querySelectorAll('.item-row'); | |
| rows.forEach(row => { | |
| const description = row.querySelector('.description').value || 'Item description'; | |
| const quantity = row.querySelector('.quantity').value || '1'; | |
| const price = row.querySelector('.price').value || '0.00'; | |
| const total = row.querySelector('.item-total').textContent; | |
| itemsHTML += ` | |
| <tr> | |
| <td class="py-2">${description}</td> | |
| <td class="text-right py-2">${quantity}</td> | |
| <td class="text-right py-2">$${parseFloat(price).toFixed(2)}</td> | |
| <td class="text-right py-2">${total}</td> | |
| </tr> | |
| `; | |
| }); | |
| invoiceContent.innerHTML = ` | |
| <div class="invoice-header flex justify-between mb-8"> | |
| <div> | |
| <h1 class="text-3xl font-bold text-indigo-600">${fromName}</h1> | |
| <p class="text-gray-600">${fromAddress}</p> | |
| <p class="text-gray-600">${fromCityStateZip}</p> | |
| <p class="text-gray-600">${fromEmail}</p> | |
| <p class="text-gray-600">${fromPhone}</p> | |
| </div> | |
| <div class="text-right"> | |
| <h2 class="text-2xl font-bold text-gray-800">INVOICE</h2> | |
| <p class="text-gray-600">Date: ${invoiceDate}</p> | |
| <p class="text-gray-600">Invoice #: ${invoiceNumber}</p> | |
| </div> | |
| </div> | |
| <div class="flex justify-between mb-8"> | |
| <div class="bill-to"> | |
| <h3 class="font-bold text-gray-700 mb-2">Bill To:</h3> | |
| <p class="text-gray-600">${toName}</p> | |
| <p class="text-gray-600">${toAddress}</p> | |
| <p class="text-gray-600">${toCityStateZip}</p> | |
| <p class="text-gray-600">${toEmail}</p> | |
| <p class="text-gray-600">${toPhone}</p> | |
| </div> | |
| </div> | |
| <table class="w-full mb-8"> | |
| <thead> | |
| <tr class="border-b border-gray-300"> | |
| <th class="text-left py-2 font-semibold text-gray-700">Description</th> | |
| <th class="text-right py-2 font-semibold text-gray-700">Qty</th> | |
| <th class="text-right py-2 font-semibold text-gray-700">Unit Price</th> | |
| <th class="text-right py-2 font-semibold text-gray-700">Amount</th> | |
| </tr> | |
| </thead> | |
| <tbody> | |
| ${itemsHTML} | |
| </tbody> | |
| </table> | |
| <div class="flex justify-end"> | |
| <div class="w-1/3"> | |
| <div class="flex justify-between py-2"> | |
| <span class="font-semibold text-gray-700">Subtotal:</span> | |
| <span>${subtotal}</span> | |
| </div> | |
| <div class="flex justify-between py-2"> | |
| <span class="font-semibold text-gray-700">Tax (${taxRate}%):</span> | |
| <span>${taxAmount}</span> | |
| </div> | |
| <div class="border-t border-gray-300 my-2"></div> | |
| <div class="flex justify-between py-2 font-bold text-lg"> | |
| <span>Total:</span> | |
| <span>${totalAmount}</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-12 pt-8 border-t border-gray-300 text-center text-gray-500"> | |
| <p>Thank you for your business!</p> | |
| <p class="mt-2">Please make payment within 30 days</p> | |
| </div> | |
| `; | |
| invoiceModal.classList.remove('hidden'); | |
| } | |
| }); |