jQuery(document).ready(function($) { // Check if we're on the builder page to avoid conflicts with invoice-builder.js const isBuilderPage = window.location.href.indexOf('easy-invoice-new') !== -1 || window.location.href.indexOf('page=easy-invoice-new') !== -1 || window.location.href.indexOf('easy-invoice-edit') !== -1 || window.location.href.indexOf('page=easy-invoice-edit') !== -1; // If we're on the builder page, don't initialize the old invoice builder // as it will conflict with the new invoice-builder.js if (isBuilderPage) { return; } // Initialize invoice builder function initInvoiceBuilder() { // Wait for the template to be available const template = document.getElementById('invoice-item-template'); if (!template) { // Invoice item template not found return; } // Initialize tabs initTabs(); // Add event listener for "Add Item" button - now handled in invoice-builder.js // Add event listener for removing items $(document).on('click', '.remove-item', function() { const item = $(this).closest('.invoice-item'); item.addClass('opacity-0'); setTimeout(() => { item.remove(); updatePreview(); }, 200); }); // Add event listener for collapsing/expanding items $(document).on('click', '.item-collapse-toggle', function() { const item = $(this).closest('.invoice-item'); const itemContent = item.find('.item-content'); const summaryElement = item.find('.item-collapsed-summary'); const icon = $(this).find('i'); // Update the summary before collapsing if (itemContent.is(':visible')) { updateItemSummary(item); // Collapse itemContent.slideUp(200); summaryElement.slideDown(200); icon.removeClass('fa-chevron-down').addClass('fa-chevron-right'); } else { // Expand itemContent.slideDown(200); summaryElement.slideUp(200); icon.removeClass('fa-chevron-right').addClass('fa-chevron-down'); } }); // Add event listeners for all form inputs $('#invoice-form').on('input', 'input, textarea', function() { updatePreview(); }); // Add event listeners for taxable item clicking $(document).on('click', '.taxable-item-container', function(e) { // Don't toggle if clicking directly on the checkbox (let the default behavior handle it) if (e.target.type === 'checkbox') { return; } // Prevent default behavior if this is a label click to avoid double toggling if (e.target.tagName.toLowerCase() === 'label') { e.preventDefault(); } const checkbox = $(this).find('input[type="checkbox"]'); checkbox.prop('checked', !checkbox.prop('checked')); updatePreview(); }); // Add event listener for taxable checkbox changes $(document).on('change', 'input[name="item-taxable[]"]', function() { updatePreview(); }); // Add auto-calculate for quantity and price $(document).on('input', 'input[name="item-quantity[]"], input[name="item-price[]"]', function() { const item = $(this).closest('.invoice-item'); calculateItemTotal(item); }); // Add keyboard shortcuts $(document).on('keydown', function(e) { // Ctrl/Cmd + Enter to save if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); $('button:contains("Save Draft")').click(); } // Ctrl/Cmd + Shift + Enter to send if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'Enter') { e.preventDefault(); $('button:contains("Send Invoice")').click(); } }); // Initialize with one item } // Initialize tab functionality function initTabs() { // Check if we're on an invoice page - if so, let invoice-builder.js handle tabs if (window.location.href.includes('easy-invoice-builder') || window.location.href.includes('easy-invoice-new') || $('.invoice-item').length > 0) { return; } // Add click event listeners to tab buttons $('.tab-button').on('click', function() { const tabId = $(this).data('tab'); // Remove active class from all tabs and buttons $('.tab-button').removeClass('active'); $('.tab-button').removeClass('text-indigo-600').addClass('text-gray-500'); $('.tab-button').removeClass('border-indigo-500').addClass('border-transparent'); $('.tab-content').removeClass('active').addClass('hidden'); // Add active class to clicked tab and its content $(this).addClass('active'); $(this).removeClass('text-gray-500').addClass('text-indigo-600'); $(this).removeClass('border-transparent').addClass('border-indigo-500'); $('#' + tabId).removeClass('hidden').addClass('active'); // If the items tab is active, make sure the items are properly sized if (tabId === 'items-tab') { updateItemSummaries(); } }); // Handle tab navigation via keyboard $('.tab-button').on('keydown', function(e) { // Arrow right or arrow down if (e.key === 'ArrowRight' || e.key === 'ArrowDown') { e.preventDefault(); const nextTab = $(this).next('.tab-button'); if (nextTab.length) { nextTab.click(); nextTab.focus(); } } // Arrow left or arrow up if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); const prevTab = $(this).prev('.tab-button'); if (prevTab.length) { prevTab.click(); prevTab.focus(); } } }); } // Update all item summaries function updateItemSummaries() { $('.invoice-item').each(function() { updateItemSummary($(this)); }); } // Add a new invoice item // Calculate total for a single item function calculateItemTotal(item) { const quantity = parseFloat(item.find('input[name="item-quantity[]"]').val()) || 0; const price = parseFloat(item.find('input[name="item-price[]"]').val()) || 0; const isTaxable = item.find('input[name="item-taxable[]"]').is(':checked'); const pricesIncludeTax = $('#prices-include-tax').val() === 'yes'; const taxRate = parseFloat($('#tax-rate').val()) || 0; let total = quantity * price; // We don't need to adjust the displayed total in the item form // The tax/non-tax calculation is handled in the updatePreview function item.find('input[name="item-total[]"]').val(total.toFixed(2)); // Update the summary fields updateItemSummary(item); updatePreview(); } // Update the collapsed summary of an item function updateItemSummary(item) { const quantity = parseFloat(item.find('input[name="item-quantity[]"]').val()) || 0; const price = parseFloat(item.find('input[name="item-price[]"]').val()) || 0; const total = quantity * price; item.find('.quantity-summary').text(quantity); item.find('.price-summary').text(price.toFixed(2)); item.find('.total-summary').text(total.toFixed(2)); } // Update the preview panel function updatePreview() { // Add loading state $('#preview-items').addClass('loading'); // Update customer info $('#preview-customer-name').text($('#customer-name').val() || 'Customer Name'); $('#preview-customer-email').text($('#customer-email').val() || 'customer@example.com'); // Update invoice details $('#preview-invoice-number').text($('#invoice-number').val()); $('#preview-invoice-date').text(formatDate($('#invoice-date').val())); $('#preview-due-date').text(formatDate($('#due-date').val())); $('#preview-invoice-title').text($('#invoice-title').val() || 'Invoice Title'); $('#preview-invoice-description').text($('#invoice-description').val() || 'Invoice description will appear here.'); // Get tax settings const taxRate = parseFloat($('#tax-rate').val()) || 0; const pricesIncludeTax = $('#prices-include-tax').val() === 'yes'; // Update items let subtotal = 0; let taxableSubtotal = 0; const previewItemsContainer = $('#preview-items'); previewItemsContainer.empty(); $('.invoice-item').each(function() { const title = $(this).find('input[name="item-title[]"]').val() || 'Item Title'; const description = $(this).find('textarea[name="item-description[]"]').val() || ''; const quantity = parseFloat($(this).find('input[name="item-quantity[]"]').val()) || 0; let price = parseFloat($(this).find('input[name="item-price[]"]').val()) || 0; const isTaxable = $(this).find('input[name="item-taxable[]"]').is(':checked'); // If prices include tax and this item is taxable, we need to extract the tax // to get the real pre-tax price for calculations let displayPrice = price; if (pricesIncludeTax && isTaxable) { price = price / (1 + (taxRate / 100)); } const total = quantity * price; subtotal += total; if (isTaxable) { taxableSubtotal += total; } const itemRow = `
${title} ${isTaxable ? '' : ''}
${description ? `
${description}
` : ''} ${quantity} $${pricesIncludeTax ? displayPrice.toFixed(2) : price.toFixed(2)} $${(quantity * (pricesIncludeTax ? displayPrice : price)).toFixed(2)} `; previewItemsContainer.append(itemRow); }); // Get discount and tax values const discountValue = parseFloat($('#discount').val()) || 0; const discountType = $('#discount-type').val(); const calculationMethod = $('#calculation-method').val(); // Calculate discount amount based on type let discountAmount = 0; if (discountType === 'percentage') { discountAmount = subtotal * (discountValue / 100); } else { // fixed amount discountAmount = Math.min(discountValue, subtotal); // Can't discount more than subtotal } // Calculate tax and total based on calculation method let taxableAmount = 0; let tax = 0; let total = 0; if (calculationMethod === 'before_tax') { // Apply discount before calculating tax // Distribute discount proportionally between taxable and non-taxable items let taxableDiscount = 0; if (subtotal > 0) { taxableDiscount = discountAmount * (taxableSubtotal / subtotal); } taxableAmount = taxableSubtotal - taxableDiscount; tax = taxableAmount * (taxRate / 100); total = subtotal - discountAmount + tax; } else { // after_tax // Calculate tax first, then apply discount tax = taxableSubtotal * (taxRate / 100); total = subtotal + tax - discountAmount; } // For display purposes, if prices already include tax, we need to adjust the subtotal and tax display let displaySubtotal = subtotal; let displayTax = tax; if (pricesIncludeTax) { // If prices include tax, the subtotal shown should be the sum of entered prices // which already include tax for taxable items displaySubtotal = 0; $('.invoice-item').each(function() { const quantity = parseFloat($(this).find('input[name="item-quantity[]"]').val()) || 0; const price = parseFloat($(this).find('input[name="item-price[]"]').val()) || 0; displaySubtotal += quantity * price; }); // The tax shown is the portion of the entered prices that represents tax displayTax = taxableSubtotal * (taxRate / 100); // Adjust the total if we're using the "before tax" calculation method if (calculationMethod === 'before_tax') { // The total remains the same, but the displayed components change total = displaySubtotal - discountAmount; } } // Ensure total is not negative total = Math.max(0, total); // Update preview elements $('#preview-subtotal').text('$' + displaySubtotal.toFixed(2)); $('#preview-discount').text('$' + discountAmount.toFixed(2)); $('#preview-tax-rate').text(taxRate); $('#preview-tax').text('$' + displayTax.toFixed(2)); $('#preview-total').text('$' + total.toFixed(2)); // Update notes and terms $('#preview-notes').text($('#notes').val() || 'Thank you for your business!'); $('#preview-terms').text($('#terms').val() || 'Payment is due within 30 days. Please make checks payable to Company Name.'); // Remove loading state setTimeout(() => { $('#preview-items').removeClass('loading'); }, 300); } // Format date as MMMM D, YYYY function formatDate(dateString) { if (!dateString) return ''; const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric' }); } // Initialize all components function init() { initInvoiceBuilder(); } // Start initialization init(); }); function initSettingsForm() { const $ = jQuery; // Handle company logo upload $('.settings-page').on('click', '.upload-logo', function(e) { e.preventDefault(); const image = wp.media({ title: 'Upload Company Logo', multiple: false }).open() .on('select', function() { const uploadedImage = image.state().get('selection').first(); const imageUrl = uploadedImage.toJSON().url; // Update logo preview $('.company-logo-preview').attr('src', imageUrl); // Store image URL in hidden field $('#company-logo-url').val(imageUrl); }); }); // Handle form submission $('.settings-form').on('submit', function(e) { e.preventDefault(); const formData = $(this).serialize(); $.ajax({ url: easyInvoice.ajaxUrl, type: 'POST', data: { action: 'save_easy_invoice_settings', nonce: easyInvoice.nonce, ...formData }, success: function(response) { if (response.success) { // Show success message if (typeof EasyInvoiceToast !== 'undefined') { EasyInvoiceToast.show('success', 'Settings saved successfully!'); } } else { // Show error message if (typeof EasyInvoiceToast !== 'undefined') { EasyInvoiceToast.show('error', 'Error saving settings. Please try again.'); } } }, error: function() { if (typeof EasyInvoiceToast !== 'undefined') { EasyInvoiceToast.show('error', 'Error saving settings. Please try again.'); } } }); }); }