# easy-invoice/2.3.6/assets/js/invoice-builder.js

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.6. 1,410 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.6/code/assets/js/invoice-builder.js
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.6/raw/assets/js/invoice-builder.js
- Modified: 2026-04-16T03:03:30+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.6/code/assets/js/invoice-builder.js#L10-L20`.

```javascript
/**
 * Invoice Builder for Easy Invoice
 * Handles invoice items, client selection, and form management
 */

(function($) {
    'use strict';
    
    // Check if we're on an invoice page
    const isInvoicePage = window.location.href.includes('easy-invoice-builder') || 
                         window.location.href.includes('easy-invoice-new') ||
                         $('.invoice-item').length > 0;
    
    if (!isInvoicePage) {
        return;
    }

    // Invoice Builder object
    window.EasyInvoiceBuilder = {
        // Default values
        settings: {
            itemCounter: 0,
            items: [],
            editMode: false,
            invoiceId: 0
        },

        // Initialize the invoice builder
        init: function() {
            // Initialize field helpers for dynamic field handling
            this.initFieldHelpers();
            
            // Set up event handlers
            this.setupEventHandlers();
            
            // Initialize existing items
            this.initializeExistingItems();
            
            // Set up payment manager
            if (window.EasyInvoicePayment) {
                window.EasyInvoicePayment.init();
            }
        },

        // Set up event handlers for invoice-related elements
        setupEventHandlers: function() {
            var self = this;
            
            // Add item button
            // First unbind any existing click handlers to prevent duplication
            $('.add-item-button').off('click').on('click', function(e) {
                e.preventDefault();
                self.addNewItem();
            });
            
            // Collapse all items button - use event delegation since it might be in a hidden tab
            $(document).off('click', '#collapse-all-items').on('click', '#collapse-all-items', function(e) {
                e.preventDefault();
                e.stopPropagation(); // Prevent event bubbling
                self.collapseAllItems();
            });
            
            // Add sample items button
            // $('#add-sample-items').off('click').on('click', function(e) {
            //     e.preventDefault();
            //     e.stopPropagation(); // Prevent event bubbling
            //     self.addSampleItems();
            // });
            
            // Individual item sample data buttons (delegate to handle dynamically added buttons)
            $(document).off('click', '.fill-sample-data-btn').on('click', '.fill-sample-data-btn', function(e) {
                e.preventDefault();
                e.stopPropagation();
                var $item = $(this).closest('.invoice-item');
                self.fillItemWithSampleData($item);
            });
            
            // Individual item collapse toggles - use event delegation as fallback
            $(document).off('click', '.item-collapse-toggle').on('click', '.item-collapse-toggle', function(e) {
                e.preventDefault();
                e.stopPropagation();
                
                var $item = $(this).closest('.invoice-item');
                
                var itemContent = $item.find('.item-content');
                var summaryElement = $item.find('.item-collapsed-summary');
                var sampleButton = $item.find('.fill-sample-data-btn');
                var icon = $(this).find('i');
                
                if (itemContent.is(':visible')) {
                    // Collapsing - update summary and change icon
                    self.updateItemSummary($item);
                    itemContent.slideUp(200);
                    summaryElement.slideDown(200);
                    sampleButton.hide(); // Hide sample button when collapsed
                    icon.removeClass('fa-chevron-down').addClass('fa-chevron-right');
                    // Add compact styling to the collapsed item
                    $item.addClass('collapsed-item');
                } else {
                    // Expanding - hide summary and change icon
                    itemContent.slideDown(200);
                    summaryElement.slideUp(200);
                    sampleButton.show(); // Show sample button when expanded
                    icon.removeClass('fa-chevron-right').addClass('fa-chevron-down');
                    // Remove compact styling from the expanded item
                    $item.removeClass('collapsed-item');
                }
                
                // Update the preview to reflect changes
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                } else if (typeof updatePreview === 'function') {
                    updatePreview();
                }
            });
            
            // Send invoice button
            $('#send_invoice').off('click').on('click', function(e) {
                e.preventDefault();
                self.sendInvoice();
            });
            
            // Reset form button
            $('#reset_form').off('click').on('click', function(e) {
                e.preventDefault();
                if (confirm('Are you sure you want to reset the form? All unsaved changes will be lost.')) {
                    self.resetForm();
                }
            });
            
            // Handle tab navigation
            $('.tab-button').off('click').on('click', function(e) {
                e.preventDefault();
                var targetTab = $(this).data('tab');
                
                // Hide all tabs
                $('.tab-content').removeClass('active').addClass('hidden');
                
                // Remove active class and reset border styling for all tabs
                $('.tab-button').removeClass('active')
                    .removeClass('border-indigo-500 text-indigo-600')
                    .addClass('border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300');
                
                // Show the target tab
                $('#' + targetTab).removeClass('hidden').addClass('active');
                
                // Add active class and update border styling to clicked tab
                $(this).addClass('active')
                    .removeClass('border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300')
                    .addClass('border-indigo-500 text-indigo-600');
                
                // If switching to items tab, ensure collapse button is properly bound
                if (targetTab === 'items-tab') {
                    setTimeout(function() {
                        if ($('#collapse-all-items').length > 0) {
                            // Re-attach event handler to ensure it works
                            $('#collapse-all-items').off('click').on('click', function(e) {
                                e.preventDefault();
                                e.stopPropagation();
                                EasyInvoiceBuilder.collapseAllItems();
                            });
                        } else {
                            // Collapse button not found in items tab
                        }
                    }, 100);
                }
            });
        },

        // Set up proper styling for the initially active tab
        setupInitialTabState: function() {
            // Find the tab that has the 'active' class
            var $activeTab = $('.tab-button.active');
            
            // If no active tab is found, default to the first tab
            if ($activeTab.length === 0) {
                $activeTab = $('.tab-button').first();
                $activeTab.addClass('active');
            }
            
            // Apply the correct styling to the active tab
            $activeTab.removeClass('border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300')
                      .addClass('border-indigo-500 text-indigo-600');
            
            // Show the corresponding tab content
            var targetTab = $activeTab.data('tab');
            $('.tab-content').removeClass('active').addClass('hidden');
            $('#' + targetTab).removeClass('hidden').addClass('active');
        },

        // Initialize invoice items from saved data or create a default empty item
        // REMOVED: Items should be initialized from PHP/HTML, not JavaScript

        // Add a new empty item
        addNewItem: function() {
            
            // Get the template
            var template = document.getElementById('invoice-item-template');
            if (!template) {
                // Invoice item template not found
                return null;
            }
            
            // Clone the template
            var clone = template.content.cloneNode(true);
            var newItem = $(clone);
            
            // Get the next item index
            var itemIndex = this.settings.itemCounter++;
            
            // Generate a unique ID for the item
            var itemId = 'item_' + Date.now() + '_' + itemIndex;
            
            // Update item ID
            newItem.find('.invoice-item').attr('id', itemId);
            
            // Update field indices to use the correct item index
            this.updateItemFieldIndices(newItem, itemIndex);
            
            // Set header to 'New Item' for newly added items
            newItem.find('h3').text('New Item');
            
            // Add the item to the container
            $('.invoice-items-container').append(newItem);
            
            // Set up event handlers
            this.setupItemEvents($('#' + itemId));
            
            this.updateItemNumbers();
            
            // Update totals
            if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                window.EasyInvoicePayment.updateTotals();
            }
            
            return $('#' + itemId);
        },
        
        // Update field indices in an item to use the correct item index
        updateItemFieldIndices: function($item, itemIndex) {
            
            // Update all input fields - match both items[0][fieldname] and items[-1][fieldname]
            $item.find('input[name*="items["]').each(function() {
                var oldName = $(this).attr('name');
                var newName = oldName.replace(/items\[(?:-1|0)\]\[/, 'items[' + itemIndex + '][');
                $(this).attr('name', newName);
            });
            
            // Update all textarea fields - match both items[0][fieldname] and items[-1][fieldname]
            $item.find('textarea[name*="items["]').each(function() {
                var oldName = $(this).attr('name');
                var newName = oldName.replace(/items\[(?:-1|0)\]\[/, 'items[' + itemIndex + '][');
                $(this).attr('name', newName);
            });
            
            // Update all select fields - match both items[0][fieldname] and items[-1][fieldname]
            $item.find('select[name*="items["]').each(function() {
                var oldName = $(this).attr('name');
                var newName = oldName.replace(/items\[(?:-1|0)\]\[/, 'items[' + itemIndex + '][');
                $(this).attr('name', newName);
            });
            
            // Update field IDs to be unique (handle both _-1 and _0)
            $item.find('[id*="_-1"], [id*="_0"]').each(function() {
                var oldId = $(this).attr('id');
                var newId = oldId.replace(/_(?:-1|0)/, '_' + itemIndex);
                $(this).attr('id', newId);
                // Update corresponding label for attribute
                var $label = $item.find('label[for="' + oldId + '"]');
                if ($label.length) {
                    $label.attr('for', newId);
                }
            });
        },
        
        // Set up event handlers for a specific item
        setupItemEvents: function($item) {
            var self = this;
            
            // Handle quantity, price, and adjustment percentage changes
            $item.find('input[name*="[quantity]"], input[name*="[price]"], input[name*="[adjust_percentage]"]').off('input').on('input', function() {
                self.calculateItemTotal($item);
            });
            
            // Remove item button
            $item.find('.remove-item').off('click').on('click', function() {
                self.removeItem($item);
            });
            
            // Handle taxable checkbox
            $item.find('input[name*="[taxable]"]').off('change').on('change', function() {
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                }
            });
            
            // Handle collapse/expand - unbind previous handlers first
            var $collapseToggle = $item.find('.item-collapse-toggle');
            
            if ($collapseToggle.length === 0) {
                // No collapse toggle found for item
                return;
            }
            
            $collapseToggle.off('click').on('click', function(e) {
                e.preventDefault();
                e.stopPropagation(); // Prevent event bubbling
                
                var itemContent = $item.find('.item-content');
                var summaryElement = $item.find('.item-collapsed-summary');
                var sampleButton = $item.find('.fill-sample-data-btn');
                var icon = $(this).find('i');
                
                if (itemContent.is(':visible')) {
                    // Collapsing - update summary and change icon
                    self.updateItemSummary($item);
                    itemContent.slideUp(200);
                    summaryElement.slideDown(200);
                    sampleButton.hide(); // Hide sample button when collapsed
                    icon.removeClass('fa-chevron-down').addClass('fa-chevron-right');
                    // Add compact styling to the collapsed item
                    $item.addClass('collapsed-item');
                } else {
                    // Expanding - hide summary and change icon
                    itemContent.slideDown(200);
                    summaryElement.slideUp(200);
                    sampleButton.show(); // Show sample button when expanded
                    icon.removeClass('fa-chevron-right').addClass('fa-chevron-down');
                    // Remove compact styling from the expanded item
                    $item.removeClass('collapsed-item');
                }
                
                // Update the preview to reflect changes
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                } else if (typeof updatePreview === 'function') {
                    updatePreview();
                }
            });
            
            // Update the title in the header when title field changes
            $item.find('input[name*="[title]"]').off('input').on('input', function() {
                var title = $(this).val() || 'Invoice Item';
                var shortTitle = title.length > 30 ? title.substring(0, 30) + '...' : title;
                $item.find('h3').text(shortTitle);
            });
        },
        
        // Calculate total for a specific item
        calculateItemTotal: function($item) {
            // Use dynamic field helpers for calculation
            if (window.EasyInvoiceFieldHelpers && window.EasyInvoiceFieldHelpers.calculateFieldValue) {
                var total = window.EasyInvoiceFieldHelpers.calculateFieldValue('total', $item);
                if (total !== null) {
                    window.EasyInvoiceFieldHelpers.setFieldValue('total', total, $item);
                    
                    // Update summary fields directly
                    var quantity = window.EasyInvoiceFieldHelpers.getFieldValue('quantity', $item);
                    var price = window.EasyInvoiceFieldHelpers.getFieldValue('price', $item);
                    
                    $item.find('.quantity-summary').text(quantity || '0');
                    $item.find('.price-summary').text((parseFloat(price) || 0).toFixed(2));
                    $item.find('.total-summary').text((parseFloat(total) || 0).toFixed(2));
                }
            } else {
                // Fallback to hardcoded calculation
                var quantity = parseFloat($item.find('input[name*="[quantity]"]').val()) || 0;
                var price = parseFloat($item.find('input[name*="[price]"]').val()) || 0;
                var adjustPercentage = 0;
                // Only apply adjust percentage if the adjust field is enabled
                if (window.easyInvoice && window.easyInvoice.showAdjustField) {
                    adjustPercentage = parseFloat($item.find('input[name*="[adjust_percentage]"]').val()) || 0;
                }
                var baseTotal = quantity * price;
                var total = baseTotal * (1 + adjustPercentage / 100);
                
                // Update the total field
                var $totalField = $item.find('input[name*="[total]"]');
                $totalField.val(total.toFixed(2));
                
                // Update the collapsed summary
                $item.find('.quantity-summary').text(quantity);
                $item.find('.price-summary').text(price.toFixed(2));
                $item.find('.total-summary').text(total.toFixed(2));
            }
        },
        
        // Update the collapsed summary of an item
        updateItemSummary: function($item) {
            // Use dynamic field helpers for summary updates
            if (window.EasyInvoiceFieldHelpers) {
                var quantity = window.EasyInvoiceFieldHelpers.getFieldValue('quantity', $item);
                var price = window.EasyInvoiceFieldHelpers.getFieldValue('price', $item);
                var total = window.EasyInvoiceFieldHelpers.getFieldValue('total', $item);
                
                // Update summary fields directly since updateSummaryFields doesn't exist
                $item.find('.quantity-summary').text(quantity || '0');
                $item.find('.price-summary').text((parseFloat(price) || 0).toFixed(2));
                $item.find('.total-summary').text((parseFloat(total) || 0).toFixed(2));
                
            } else {
                // Fallback to hardcoded summary
                var quantity = parseFloat($item.find('input[name*="[quantity]"]').val()) || 0;
                var price = parseFloat($item.find('input[name*="[price]"]').val()) || 0;
                var adjustPercentage = 0;
                // Only apply adjust percentage if the adjust field is enabled
                if (window.easyInvoice && window.easyInvoice.showAdjustField) {
                    adjustPercentage = parseFloat($item.find('input[name*="[adjust_percentage]"]').val()) || 0;
                }
                var baseTotal = quantity * price;
                var total = baseTotal * (1 + adjustPercentage / 100);
                
                $item.find('.quantity-summary').text(quantity);
                $item.find('.price-summary').text(price.toFixed(2));
                $item.find('.total-summary').text(total.toFixed(2));
                
            }
        },

        // Remove an item from the invoice
        removeItem: function($item) {
            var self = this;
            $item.addClass('opacity-0');
            setTimeout(function() {
                $item.remove();
                
                // Ensure at least one item remains
                if ($('.invoice-item').length === 0) {
                    self.addNewItem();
                } else {
                    self.updateItemNumbers();
                }
                
                // Update totals
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                }
            }.bind(this), 200);
        },

        updateItemNumbers: function() {
            var self = this;
            $('.invoice-items-container .invoice-item').each(function(index) {
                var itemNumber = index + 1;
                var title = $(this).find('input[name*="[title]"]').val();
                var shortTitle = title ? (title.length > 30 ? title.substring(0, 30) + '...' : title) : 'New Item';
                $(this).find('h3').text(shortTitle);
                
                // Only update field indices if they don't already match the current index
                var $item = $(this);
                var firstField = $item.find('input[name*="items["]').first();
                if (firstField.length > 0) {
                    var fieldName = firstField.attr('name');
                    var currentIndex = fieldName.match(/items\[(\d+)\]/);
                    if (currentIndex && parseInt(currentIndex[1]) !== index) {
                        // Field index doesn't match, update it
                        self.updateItemFieldIndices($item, index);
                    }
                }
            });
            self.settings.itemCounter = $('.invoice-items-container .invoice-item').length;
        },

        // Format currency value
        formatCurrency: function(value) {
            var symbol = '$';
            
            // Use the currency symbol from payment manager if available
            if (window.EasyInvoicePayment && window.EasyInvoicePayment.settings.currencySymbol) {
                symbol = window.EasyInvoicePayment.settings.currencySymbol;
            }
            
            return symbol + parseFloat(value).toFixed(2);
        },

        // Get all current invoice items
        getItems: function() {
            var items = [];
            
            $('.invoice-item').each(function(index) {
                var $item = $(this);
                var item = {
                    id: $item.attr('id')
                };
                
                // Use dynamic field helpers to get all field values
                if (window.EasyInvoiceFieldHelpers && window.EasyInvoiceFieldConfig) {
                    Object.keys(window.EasyInvoiceFieldConfig).forEach(function(fieldName) {
                        var value = window.EasyInvoiceFieldHelpers.getFieldValue(fieldName, $item);
                        item[fieldName] = value;
                    });
                } else {
                    // Fallback to hardcoded field names
                    item.name = $item.find('input[name*="[title]"]').val() || '';
                    item.description = $item.find('textarea[name*="[description]"]').val() || '';
                    item.quantity = parseFloat($item.find('input[name*="[quantity]"]').val()) || 0;
                    item.price = parseFloat($item.find('input[name*="[price]"]').val()) || 0;
                    item.taxable = $item.find('input[name*="[taxable]"]').is(':checked');
                }
                
                items.push(item);
            });
            
            return items;
        },

        // Set up client selection functionality
        setupClientSelection: function() {
            var self = this;
            var isInitialLoad = true; // Flag to prevent AJAX calls on initial load
            
            // Client dropdown change
            $('#client_id').on('change', function() {
                var clientId = $(this).val();
                
                // Skip AJAX call if this is the initial load
                if (isInitialLoad) {
                    isInitialLoad = false;
                    return;
                }
                
                if (clientId === 'new') {
                    // Show new client modal
                    $('#add_client_modal').show();
                } else if (clientId !== '') {
                    // Load client data (ajax call or from already available data)
                    self.loadClientData(clientId);
                }
            });
            
            // Close modal button
            $('.close-modal').on('click', function() {
                $('#add_client_modal').hide();
                
                // Reset client dropdown if no client was selected
                if ($('#client_id').val() === 'new') {
                    $('#client_id').val('');
                }
            });
            
            // Submit new client form
            $('#add_client_form').on('submit', function(e) {
                e.preventDefault();
                self.addNewClient();
            });
            
            // Reset the flag after a short delay to allow for user interactions
            setTimeout(function() {
                isInitialLoad = false;
            }, 500);
        },

        // Load client data when a client is selected
        loadClientData: function(clientId) {
            // First check if we have client data already loaded from PHP
            if (typeof easyInvoice !== 'undefined' && easyInvoice.clientData && easyInvoice.clientData.id == clientId) {
                var client = easyInvoice.clientData;
                
                // Update client info fields
                $('#client_name_display').text(client.name || '');
                $('#client_email_display').text(client.email || '');
                $('#client_phone_display').text(client.phone || '');
                $('#client_address_display').html((client.address || '').replace(/\n/g, '<br>'));
                
                // Show client info section
                $('#client_info').show();
                return;
            }
            
            // For dynamic client selection (not initial load), make an AJAX call to get client data
            $.ajax({
                url: easyInvoice.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'easy_invoice_get_client',
                    nonce: easyInvoice.nonce,
                    client_id: clientId
                },
                success: function(response) {
                    if (response.success) {
                        var client = response.data;
                        
                        // Update client info fields
                        $('#client_name_display').text(client.name);
                        $('#client_email_display').text(client.email);
                        $('#client_phone_display').text(client.phone || '');
                        $('#client_address_display').html(client.address.replace(/\n/g, '<br>') || '');
                        
                        // Show client info section
                        $('#client_info').show();
                    } else {
                        // Error loading client data
                    }
                },
                error: function(xhr, status, error) {
                    // AJAX error loading client data
                }
            });
        },

        // Add a new client via AJAX
        addNewClient: function() {
            var self = this;
            var clientData = {
                name: $('#new_client_name').val(),
                email: $('#new_client_email').val(),
                phone: $('#new_client_phone').val(),
                address: $('#new_client_address').val(),
                notes: $('#new_client_notes').val()
            };
            
            // Validate required fields
            if (!clientData.name || !clientData.email) {
                if (typeof EasyInvoiceToast !== 'undefined') {
                    EasyInvoiceToast.show('error', 'Client name and email are required.');
                }
                return;
            }
            
            // Send AJAX request to add client
            $.ajax({
                url: easyInvoice.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'easy_invoice_add_client',
                    nonce: easyInvoice.nonce,
                    client_data: clientData
                },
                success: function(response) {
                    if (response.success) {
                        var newClient = response.data.client;
                        var newClientId = response.data.client_id;
                        
                        // Add new client to dropdown
                        $('#client_id').append($('<option>', {
                            value: newClientId,
                            text: newClient.name
                        }));
                        
                        // Select the new client
                        $('#client_id').val(newClientId);
                        
                        // Load the client data
                        self.loadClientData(newClientId);
                        
                        // Hide modal
                        $('#add_client_modal').hide();
                        
                        // Clear form
                        $('#add_client_form')[0].reset();
                        
                        // Show success message
                        if (typeof EasyInvoiceToast !== 'undefined') {
                            EasyInvoiceToast.show('success', 'Client added successfully!');
                        }
                    } else {
                        if (typeof EasyInvoiceToast !== 'undefined') {
                            EasyInvoiceToast.show('error', 'Error adding client: ' + response.data);
                        }
                    }
                },
                error: function(xhr, status, error) {
                    // AJAX error adding client
                    if (typeof EasyInvoiceToast !== 'undefined') {
                        EasyInvoiceToast.show('error', 'Error adding client. Please try again.');
                    }
                }
            });
        },

        // Save the invoice
        saveInvoice: function() {
            var self = this;
            
            // Collect all form data at once
            var formData = {};
            
            // Get all form fields using serializeArray
            $('#invoice-form').serializeArray().forEach(function(item) {
                formData[item.name] = item.value;
            });
            
            // Add items data
            formData.items = this.getItems();
            
            // Only exclude invoice number for updates, not for new invoices
            if (this.settings.editMode && this.settings.invoiceId > 0) {
                delete formData['invoice-number'];
                delete formData.invoice_number;
            }
            
            // Add invoice ID if in edit mode
            if (this.settings.editMode && this.settings.invoiceId > 0) {
                formData.invoice_id = this.settings.invoiceId;
            }
            
            // Add client ID if selected
            var clientId = $('#client_id').val();
            if (clientId && clientId !== '') {
                formData.client_id = clientId;
            }
            
            // Show loading state
            var $saveBtn = $('.save-invoice-btn');
            var originalText = $saveBtn.text();
            $saveBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin mr-2"></i>Saving...');
            
            // Send AJAX request to save invoice
            $.ajax({
                url: easyInvoice.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'easy_invoice_save_invoice',
                    nonce: easyInvoice.nonce,
                    invoice_data: formData
                },
                success: function(response) {
                    // Restore button state
                    $saveBtn.prop('disabled', false).text(originalText);
                    
                    if (response.success) {
                        // Show success message without page reload
                        self.showNotification('Invoice saved successfully!', 'success');
                        
                        // Update invoice ID if it changed (for new invoices)
                        if (response.data.invoice_id) {
                            self.settings.invoiceId = response.data.invoice_id;
                            self.settings.editMode = true;
                            
                            // Update the URL to reflect the invoice ID
                            if (window.history && window.history.pushState) {
                                var newUrl = window.location.href.split('?')[0] + '?invoice_id=' + response.data.invoice_id;
                                window.history.pushState({}, '', newUrl);
                            }
                        }
                        
                        // Update any UI elements that depend on edit mode
                        self.updateUIForEditMode();
                        
                    } else {
                        self.showNotification('Error saving invoice: ' + response.data, 'error');
                    }
                },
                error: function(xhr, status, error) {
                    // Restore button state
                    $saveBtn.prop('disabled', false).text(originalText);
                    
                    // AJAX error saving invoice
                    self.showNotification('Error saving invoice. Please try again.', 'error');
                }
            });
        },

        // Send the invoice to the client
        sendInvoice: function() {
            // First save the invoice, then send it
            var self = this;
            
            // Collect all form data at once
            var formData = {};
            
            // Get all form fields using serializeArray
            $('#invoice-form').serializeArray().forEach(function(item) {
                formData[item.name] = item.value;
            });
            
            // Add items data
            formData.items = this.getItems();
            
            // Only exclude invoice number for updates, not for new invoices
            if (this.settings.editMode && this.settings.invoiceId > 0) {
                delete formData['invoice-number'];
                delete formData.invoice_number;
            }
            
            // Add invoice ID if in edit mode
            if (this.settings.editMode && this.settings.invoiceId > 0) {
                formData.invoice_id = this.settings.invoiceId;
            }
            
            // Add client ID if selected
            var clientId = $('#client_id').val();
            if (clientId && clientId !== '') {
                formData.client_id = clientId;
            }
            
            // Show loading state
            var $sendBtn = $('.send-invoice-btn');
            var originalText = $sendBtn.text();
            $sendBtn.prop('disabled', true).html('<i class="fas fa-spinner fa-spin mr-2"></i>Sending...');
            
            // Send AJAX request to save and send invoice
            $.ajax({
                url: easyInvoice.ajaxUrl,
                type: 'POST',
                data: {
                    action: 'easy_invoice_save_and_send_invoice',
                    nonce: easyInvoice.nonce,
                    invoice_data: formData
                },
                success: function(response) {
                    // Restore button state
                    $sendBtn.prop('disabled', false).text(originalText);
                    
                    if (response.success) {
                        // Show success message without page reload
                        self.showNotification('Invoice sent successfully!', 'success');
                        
                        // Update invoice ID if it changed (for new invoices)
                        if (response.data.invoice_id) {
                            self.settings.invoiceId = response.data.invoice_id;
                            self.settings.editMode = true;
                            
                            // Update the URL to reflect the invoice ID
                            if (window.history && window.history.pushState) {
                                var newUrl = window.location.href.split('?')[0] + '?invoice_id=' + response.data.invoice_id;
                                window.history.pushState({}, '', newUrl);
                            }
                        }
                        
                        // Update any UI elements that depend on edit mode
                        self.updateUIForEditMode();
                        
                    } else {
                        self.showNotification('Error sending invoice: ' + response.data, 'error');
                    }
                },
                error: function(xhr, status, error) {
                    // Restore button state
                    $sendBtn.prop('disabled', false).text(originalText);
                    
                    // AJAX error sending invoice
                    self.showNotification('Error sending invoice. Please try again.', 'error');
                }
            });
        },

        // Reset the form
        resetForm: function() {
            // Reset form fields
            $('#invoice-form')[0].reset();
            
            // Clear items
            $('.invoice-items-container').empty();
            
            // Add one empty item
            this.addNewItem();
            
            // Hide client info
            $('#client_info').hide();
            
            // Reset client dropdown
            $('#client_id').val('');
            
            // Reset payment settings if payment manager is available
            if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateUI === 'function') {
                window.EasyInvoicePayment.updateUI();
            }
        },

        // Collapse or expand all invoice items
        collapseAllItems: function() {
            var self = this;
            var $button = $('#collapse-all-items');
            var allCollapsed = true;
            
            // Check if all items are already collapsed
            $('.invoice-item').each(function() {
                var $item = $(this);
                var itemContent = $item.find('.item-content');
                if (itemContent.is(':visible')) {
                    allCollapsed = false;
                    return false; // Break the loop if we find an expanded item
                }
            });
            
            // Update button text based on current state
            if (allCollapsed) {
                // If all items are collapsed, expand them
                $button.html('<i class="fas fa-chevron-down mr-1"></i> Collapse All');
                $('.invoice-item').each(function() {
                    var $item = $(this);
                    var itemContent = $item.find('.item-content');
                    var summaryElement = $item.find('.item-collapsed-summary');
                    var sampleButton = $item.find('.fill-sample-data-btn');
                    var icon = $item.find('.item-collapse-toggle i');
                    
                    
                    // Only toggle if it's currently collapsed
                    if (!itemContent.is(':visible')) {
                        itemContent.slideDown(200);
                        summaryElement.slideUp(200);
                        sampleButton.show(); // Show sample button when expanded
                        icon.removeClass('fa-chevron-right').addClass('fa-chevron-down');
                        // Remove collapsed item styling
                        $item.removeClass('collapsed-item');
                    }
                });
            } else {
                // If any items are expanded, collapse them all
                $button.html('<i class="fas fa-chevron-right mr-1"></i> Expand All');
                $('.invoice-item').each(function() {
                    var $item = $(this);
                    var itemContent = $item.find('.item-content');
                    var summaryElement = $item.find('.item-collapsed-summary');
                    var sampleButton = $item.find('.fill-sample-data-btn');
                    var icon = $item.find('.item-collapse-toggle i');
                    
                    // Only toggle if it's currently expanded
                    if (itemContent.is(':visible')) {
                        // Update the summary before collapsing
                        self.updateItemSummary($item);
                        itemContent.slideUp(200);
                        summaryElement.slideDown(200);
                        sampleButton.hide(); // Hide sample button when collapsed
                        icon.removeClass('fa-chevron-down').addClass('fa-chevron-right');
                        // Add collapsed item styling
                        $item.addClass('collapsed-item');
                    }
                });
            }
            
            // Update the preview to reflect changes
            if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                window.EasyInvoicePayment.updateTotals();
            } else if (typeof updatePreview === 'function') {
                updatePreview();
            }
        },

        // Fill an item with sample data
        fillItemWithSampleData: function($item) {
            
            // Array of realistic sample data
            var sampleItems = [
                {
                    title: 'Web Development Services',
                    description: 'Custom website development including responsive design, SEO optimization, and content management system integration.',
                    quantity: 1,
                    price: 2500.00,
                    taxable: true
                },
                {
                    title: 'Logo Design Package',
                    description: 'Professional logo design with multiple concepts, revisions, and final files in various formats (AI, EPS, PNG, JPG).',
                    quantity: 1,
                    price: 450.00,
                    taxable: false
                },
                {
                    title: 'Monthly Website Maintenance',
                    description: 'Ongoing website maintenance including security updates, content updates, and technical support.',
                    quantity: 3,
                    price: 150.00,
                    taxable: true
                },
                {
                    title: 'SEO Optimization',
                    description: 'Search engine optimization services including keyword research, on-page optimization, and performance monitoring.',
                    quantity: 1,
                    price: 800.00,
                    taxable: true
                },
                {
                    title: 'Content Writing',
                    description: 'Professional content writing services including blog posts, website copy, and marketing materials.',
                    quantity: 5,
                    price: 75.00,
                    taxable: true
                },
                {
                    title: 'Social Media Management',
                    description: 'Monthly social media management including content creation, posting, and engagement monitoring.',
                    quantity: 1,
                    price: 300.00,
                    taxable: true
                }
            ];
            
            // Pick a random sample item
            var randomIndex = Math.floor(Math.random() * sampleItems.length);
            var sampleData = sampleItems[randomIndex];
            
            // Add dynamic sample values for any custom fields
            if (window.EasyInvoiceFieldConfig) {
                Object.keys(window.EasyInvoiceFieldConfig).forEach(function(fieldName) {
                    // Skip standard fields that are already in sampleData
                    if (!sampleData.hasOwnProperty(fieldName)) {
                        var fieldConfig = window.EasyInvoiceFieldConfig[fieldName];
                        var fieldType = fieldConfig.type;
                        
                        // Generate appropriate sample value based on field type
                        switch (fieldType) {
                            case 'text':
                                sampleData[fieldName] = 'Sample ' + fieldName.replace(/_/g, ' ').replace(/\b\w/g, function(l) { return l.toUpperCase(); });
                                break;
                            case 'number':
                                sampleData[fieldName] = Math.floor(Math.random() * 100) + 1;
                                break;
                            case 'checkbox':
                                sampleData[fieldName] = Math.random() > 0.5 ? '1' : '0';
                                break;
                            case 'textarea':
                                sampleData[fieldName] = 'This is a sample value for ' + fieldName.replace(/_/g, ' ') + '.';
                                break;
                            default:
                                sampleData[fieldName] = 'Sample ' + fieldName;
                                break;
                        }
                    }
                });
            }
            
            // Set values using dynamic field helpers
            if (window.EasyInvoiceFieldHelpers) {
                Object.keys(sampleData).forEach(function(fieldName) {
                    if (window.EasyInvoiceFieldConfig[fieldName]) {
                        window.EasyInvoiceFieldHelpers.setFieldValue(fieldName, sampleData[fieldName], $item);
                    }
                });
                
                // Trigger input event for title field to update header
                $item.find('input[name*="[title]"]').trigger('input');
            } else {
                // Fallback to hardcoded field names
                $item.find('input[name*="[title]"]').val(sampleData.title).trigger('input');
                $item.find('textarea[name*="[description]"]').val(sampleData.description);
                $item.find('input[name*="[quantity]"]').val(sampleData.quantity);
                $item.find('input[name*="[price]"]').val(sampleData.price);
                $item.find('input[name*="[taxable]"]').prop('checked', sampleData.taxable);
            }
            
            // Calculate total
            this.calculateItemTotal($item);
            
            // Add a small delay to ensure total calculation is complete
            setTimeout(function() {
                // Re-calculate total to ensure it's correct
                // Store reference to the correct context
                var self = this;
                self.calculateItemTotal($item);
                
                // Update totals
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                }
            }.bind(this), 100);
            
        },

        // Load invoice data when in edit mode
        loadInvoiceData: function(invoiceData) {
            
            // Set form fields using the correct field names from InvoiceFormManager
            $('#invoice-form').find('input[name="invoice_title"]').val(invoiceData.title || '');
            $('#invoice-form').find('input[name="invoice-number"]').val(invoiceData.number || '');
            $('#invoice-form').find('input[name="issue-date"]').val(invoiceData.issue_date || '');
            $('#invoice-form').find('input[name="due-date"]').val(invoiceData.due_date || '');
            $('#invoice-form').find('select[name="status"]').val(invoiceData.status || 'draft');
            $('#invoice-form').find('textarea[name="notes"]').val(invoiceData.notes || '');
            
            // Set client info if available
            if (easyInvoice.clientData) {
                $('#client_name_display').text(easyInvoice.clientData.name || '');
                $('#client_email_display').text(easyInvoice.clientData.email || '');
                $('#client_address_display').html((easyInvoice.clientData.address || '').replace(/\n/g, '<br>'));
                $('#client_info').show();
            }
            
            // Set items
            if (easyInvoice.invoiceItems && Array.isArray(easyInvoice.invoiceItems)) {
                this.settings.items = easyInvoice.invoiceItems;
            }
        },

        // Show notification
        showNotification: function(message, type) {
            // Remove any existing notifications
            $('.easy-invoice-notification').remove();
            
            // Create notification element
            var notification = $('<div class="easy-invoice-notification"></div>');
            
            // Set notification content and styling
            var icon = type === 'success' ? 'fas fa-check-circle' : 'fas fa-exclamation-circle';
            var bgColor = type === 'success' ? 'bg-green-50' : 'bg-red-50';
            var borderColor = type === 'success' ? 'border-green-200' : 'border-red-200';
            var textColor = type === 'success' ? 'text-green-800' : 'text-red-800';
            var iconColor = type === 'success' ? 'text-green-400' : 'text-red-400';
            
            notification.html(`
                <div class="fixed top-4 right-4 z-50 max-w-sm w-full ${bgColor} border ${borderColor} rounded-lg shadow-lg p-4">
                    <div class="flex items-start">
                        <div class="flex-shrink-0">
                            <i class="${icon} ${iconColor} text-lg"></i>
                        </div>
                        <div class="ml-3 w-0 flex-1">
                            <p class="text-sm font-medium ${textColor}">${message}</p>
                        </div>
                        <div class="ml-4 flex-shrink-0 flex">
                            <button class="notification-close bg-transparent rounded-md inline-flex text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                                <span class="sr-only">Close</span>
                                <i class="fas fa-times"></i>
                            </button>
                        </div>
                    </div>
                </div>
            `);
            
            // Add to page
            $('body').append(notification);
            
            // Auto-hide after 5 seconds
            setTimeout(function() {
                notification.fadeOut(300, function() {
                    $(this).remove();
                });
            }, 5000);
            
            // Handle close button
            notification.find('.notification-close').on('click', function() {
                notification.fadeOut(300, function() {
                    $(this).remove();
                });
            });
        },

        // Update UI for edit mode
        updateUIForEditMode: function() {
            // Update page title to show edit mode
            if (this.settings.editMode && this.settings.invoiceId > 0) {
                document.title = document.title.replace('New Invoice', 'Edit Invoice');
                
                // Update any buttons or UI elements that should change in edit mode
                $('.save-invoice-btn').text('Update Invoice');
                $('.send-invoice-btn').text('Update & Send');
            }
        },

        // Set up event handlers for existing items loaded from PHP
        setupExistingItems: function() {
            
            // Check if we have existing items in the container (added by PHP)
            var existingItems = $('.invoice-items-container .invoice-item');
            
            if (existingItems.length > 0) {
                var self = this;
                
                // Set the item counter to the number of existing items
                this.settings.itemCounter = existingItems.length;
                
                // Update field indices for all existing items to ensure they are sequential
                existingItems.each(function(index) {
                    var $item = $(this);
                    
                    // Update field indices to ensure they are sequential (0, 1, 2, etc.)
                    self.updateItemFieldIndices($item, index);
                    
                    // Set up event handlers for this item
                    self.setupItemEvents($item);
                });
                
                // Update item numbers and titles
                this.updateItemNumbers();
                
                // Update totals after setting up existing items
                if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
                    window.EasyInvoicePayment.updateTotals();
                }
                
                // Calculate totals for all existing items
                existingItems.each(function() {
                    self.calculateItemTotal($(this));
                });
            } else {
                // Set item counter to 0 if no existing items
                this.settings.itemCounter = 0;
            }
            
        },

        // Initialize field helpers for dynamic field handling
        initFieldHelpers: function() {
            
            // Wait a bit for the field config to be available
            var self = this;
            var attempts = 0;
            var maxAttempts = 10;
            
            function tryInitFieldHelpers() {
                attempts++;
                
            if (window.easyInvoice && window.easyInvoice.fieldConfig && window.easyInvoice.fieldConfig.itemFields) {
                window.EasyInvoiceFieldConfig = window.easyInvoice.fieldConfig.itemFields;
                
                window.EasyInvoiceFieldHelpers = {
                    getFieldValue: function(fieldName, $item) {
                        var config = window.EasyInvoiceFieldConfig[fieldName];
                        if (!config) {
                            return '';
                        }
                        
                        var fieldType = config.type;
                        
                        switch (fieldType) {
                            case 'text':
                            case 'number':
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                return $field.val() || '';
                            case 'textarea':
                                var $field = $item.find('textarea[name*="[' + fieldName + ']"]');
                                return $field.val() || '';
                            case 'checkbox':
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                return $field.is(':checked') ? '1' : '0';
                            default:
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                return $field.val() || '';
                        }
                    },
                    
                    setFieldValue: function(fieldName, value, $item) {
                        var config = window.EasyInvoiceFieldConfig[fieldName];
                        if (!config) {
                            return;
                        }
                        
                        var fieldType = config.type;
                        
                        switch (fieldType) {
                            case 'text':
                            case 'number':
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                $field.val(value);
                                break;
                            case 'textarea':
                                var $field = $item.find('textarea[name*="[' + fieldName + ']"]');
                                $field.val(value);
                                break;
                            case 'checkbox':
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                if (value === '1' || value === true || value === 'true') {
                                    $field.prop('checked', true);
                                } else {
                                    $field.prop('checked', false);
                                }
                                break;
                            default:
                                var $field = $item.find('input[name*="[' + fieldName + ']"]');
                                $field.val(value);
                                break;
                        }
                    },
                    
                    calculateFieldValue: function(fieldName, $item) {
                        if (fieldName === 'total') {
                            var quantity = parseFloat(this.getFieldValue('quantity', $item)) || 0;
                            var price = parseFloat(this.getFieldValue('price', $item)) || 0;
                            var adjustPercentage = 0;
                            // Only apply adjust percentage if the adjust field is enabled
                            if (window.easyInvoice && window.easyInvoice.showAdjustField) {
                                adjustPercentage = parseFloat(this.getFieldValue('adjust_percentage', $item)) || 0;
                            }
                            var baseTotal = quantity * price;
                            var total = baseTotal * (1 + adjustPercentage / 100);
                            return total;
                        }
                        return null;
                    }
                };
                    
                    return;
                }
                
                if (attempts < maxAttempts) {
                    setTimeout(tryInitFieldHelpers, 100);
                } else {
                    // Failed to initialize field helpers after multiple attempts
                }
            }
            
            // Start the initialization process
            tryInitFieldHelpers();
        },

        // Load client data from PHP (for initial load)
        loadClientDataFromPHP: function(clientData) {
            
            // Set the client ID in the form
            $('#client_id').val(clientData.id);
            
            // Update client display
            $('#selected-client-name').text(clientData.name || '');
            $('#selected-client-email').text(clientData.email || '');
            $('#display-client-name').text(clientData.name || '-');
            $('#display-client-company').text(clientData.company || '-');
            $('#display-client-email').text(clientData.email || '-');
            $('#display-client-phone').text(clientData.phone || '-');
            $('#display-client-website').text(clientData.website || '-');
            $('#display-client-address').text(clientData.address || '-');
            
            // Show client info sections
            $('#selected-client-display').show().removeClass('hidden');
            $('#client-info-display').show().removeClass('hidden');
            $('#no-client-message').hide().addClass('hidden');
            
            // Update the edit client button URL
            $('#edit-selected-client').attr('href', easyInvoice.adminUrl + 'admin.php?page=easy-invoice-client-edit&client_id=' + clientData.id);
        },

        // Load invoice data (for edit mode)
        loadInvoiceData: function(invoiceData) {
            
            // Set form fields with invoice data
            if (invoiceData.title) {
                $('input[name="invoice_title"]').val(invoiceData.title);
            }
            if (invoiceData.issue_date) {
                $('input[name="issue-date"]').val(invoiceData.issue_date);
            }
            if (invoiceData.due_date) {
                $('input[name="due-date"]').val(invoiceData.due_date);
            }
            if (invoiceData.status) {
                $('select[name="status"]').val(invoiceData.status);
            }
            if (invoiceData.notes) {
                $('textarea[name="notes"]').val(invoiceData.notes);
            }
            if (invoiceData.terms) {
                $('textarea[name="terms"]').val(invoiceData.terms);
            }
            if (invoiceData.internal_notes) {
                $('textarea[name="internal_notes"]').val(invoiceData.internal_notes);
            }
        },

        // Set up initial tab state
        setupInitialTabState: function() {
            // Ensure the first tab is active by default
            var $firstTab = $('.tab-button').first();
            var $firstContent = $('.tab-content').first();
            
            if ($firstTab.length && $firstContent.length) {
                $firstTab.addClass('border-indigo-500 text-indigo-600')
                    .removeClass('border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300');
                $firstContent.addClass('active').removeClass('hidden');
            }
        },

        // Initialize existing items
        initializeExistingItems: function() {
            // Check if we have the easyInvoice object
            if (typeof easyInvoice !== 'undefined') {
                this.settings.editMode = easyInvoice.editMode || false;
                this.settings.invoiceId = parseInt(easyInvoice.invoice_id) || 0;
                
                // Load invoice data if in edit mode
                if (this.settings.editMode && easyInvoice.invoiceData) {
                    this.loadInvoiceData(easyInvoice.invoiceData);
                }
                
                // Load items if available
                if (easyInvoice.invoiceItems && Array.isArray(easyInvoice.invoiceItems)) {
                    this.settings.items = easyInvoice.invoiceItems;
                }
                
                // Load initial client data if available from PHP
                if (easyInvoice.clientData && easyInvoice.clientData.id) {
                    this.loadClientDataFromPHP(easyInvoice.clientData);
                }
            } else {
                // easyInvoice object is not defined
            }
            
            // Ensure initial active tab has correct styling
            this.setupInitialTabState();
            
            // Set up event handlers for existing items loaded from PHP
            this.setupExistingItems();
            
            // Set up client selection functionality
            this.setupClientSelection();
        }
    };

    // Initialize invoice builder when document is ready
    jQuery(document).ready(function($) {
        // Initialize the invoice builder
        if (typeof EasyInvoiceBuilder !== 'undefined') {
        EasyInvoiceBuilder.init();
        }
        
        // Fallback: Try to set up event handlers again after a short delay
        // in case the DOM elements weren't ready yet
        setTimeout(function() {
            if ($('#collapse-all-items').length === 0) {
                // Collapse button still not found after delay
            } else {
                // Re-attach event handler if needed (using delegation)
                $(document).off('click', '#collapse-all-items').on('click', '#collapse-all-items', function(e) {
                    e.preventDefault();
                    e.stopPropagation();
                    EasyInvoiceBuilder.collapseAllItems();
                });
            }
        }, 1000);
    });

})(jQuery);
```
