/**
* Invoice Save Functionality
*
* Handles saving and updating invoices in the invoice builder page.
*/
(function($) {
'use strict';
function eiBuilderI18n(key, fallback) {
if (typeof easyInvoice !== 'undefined' && easyInvoice.i18n && easyInvoice.i18n[key]) {
return easyInvoice.i18n[key];
}
return fallback || '';
}
var spinnerSvg = '';
// 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-form').length > 0;
if (!isInvoicePage) {
return;
}
// Create a global object for invoice save functionality
window.EasyInvoiceSave = {
init: function() {
this.bindEvents();
// Load invoice data if in edit mode
if ($('#invoice-id').val()) {
this.loadInvoiceData();
} else {
// No invoice ID - this is a new invoice
}
},
// Clear all error states
clearErrorStates: function() {
$('.form-input, .form-select, .form-textarea').removeClass('error');
$('.field-error').remove();
},
// Apply error states to specific fields
applyErrorStates: function(errors) {
this.clearErrorStates();
if (errors && typeof errors === 'object') {
Object.keys(errors).forEach(fieldName => {
const field = $(`[name="${fieldName}"]`);
if (field.length) {
field.addClass('error');
// Add error message below the field
const errorMessage = errors[fieldName];
if (errorMessage) {
const errorHtml = `
${errorMessage}
`;
field.closest('div').append(errorHtml);
}
}
});
}
},
bindEvents: function() {
// Count save buttons to make sure it exists
const saveButtonCount = $('#save-invoice-btn').length;
// Only bind the click event if the button exists
if (saveButtonCount > 0) {
// Save invoice button click handler - jQuery method
$('#save-invoice-btn').on('click', this.handleSaveButtonClick.bind(this));
} else {
// Save button not found in the DOM
}
// Add form submit handler to trigger save button
$('#invoice-form').on('submit', function(e) {
e.preventDefault();
// Ensure all item totals are calculated before submission
$('.invoice-item').each(function() {
const $item = $(this);
if (window.EasyInvoiceBuilder && typeof window.EasyInvoiceBuilder.calculateItemTotal === 'function') {
window.EasyInvoiceBuilder.calculateItemTotal($item);
}
});
// Update overall totals
if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
window.EasyInvoicePayment.updateTotals();
}
$('#save-invoice-btn').click();
});
// Clear error states when user starts typing
$('.form-input, .form-select, .form-textarea').on('input change', function() {
const field = $(this);
if (field.hasClass('error')) {
field.removeClass('error');
field.siblings('.field-error').remove();
}
});
},
// Handle save button click event
handleSaveButtonClick: function(e) {
e.preventDefault();
this.saveInvoice();
},
// Function to save or update invoice
saveInvoice: function() {
// Show loading state
const $saveBtn = $('#save-invoice-btn');
const originalBtnText = $saveBtn.html();
$saveBtn.html('' + spinnerSvg + '' + eiBuilderI18n('saving', 'Saving…') + '');
$saveBtn.prop('disabled', true).attr('aria-busy', 'true');
// Collect form data
const formData = this.collectFormData();
// Backend validation will handle all field validation
// No need for frontend validation since we have comprehensive backend validation
// Prepare the request data
const requestData = {
action: 'easy_invoice_save_invoice',
nonce: $('#invoice_nonce').val(),
invoice_data: formData
};
// Send AJAX request
$.ajax({
url: easyInvoice.ajaxUrl,
type: 'POST',
data: requestData,
success: (response) => {
// Restore button state
$saveBtn.html(originalBtnText);
$saveBtn.prop('disabled', false).removeAttr('aria-busy');
if (response.success) {
// Clear any previous error states
this.clearErrorStates();
// If this is a new invoice (no invoice_id in form), update URL without redirect
// Do not show toast here; global handler will do it
var invId = $('#invoice-id').val();
if (response.data.invoice && response.data.invoice.id) {
$('#invoice-id').val(response.data.invoice.id);
invId = response.data.invoice.id;
}
if (invId && String(invId) !== '0') {
$saveBtn.html('' + eiBuilderI18n('update_invoice', 'Update Invoice') + '');
}
// Update form with any returned data if needed
if (response.data.invoice) {
// Update the invoice status display if available
if (response.data.invoice.status) {
$('#payment-status').val(response.data.invoice.status);
}
// Reload client data if client_id is present
if (response.data.invoice.client_id) {
// Update the global client data if we have it
if (response.data.client && typeof easyInvoice !== 'undefined') {
easyInvoice.clientData = response.data.client;
// Update client display fields directly
$('#client-name').val(response.data.client.name || '');
$('#client-email').val(response.data.client.email || '');
$('#client-phone').val(response.data.client.phone || '');
$('#client-address').val(response.data.client.address || '');
}
}
// Update any other fields if needed
}
$(document).trigger('easy-invoice-saved', [response]);
} else {
$(document).trigger('easy-invoice-save-failed', [response]);
// Show error message
const errorMessage = response.data && response.data.message ? response.data.message : 'Failed to save invoice';
if (typeof EasyInvoiceToast !== 'undefined') {
EasyInvoiceToast.show('error', errorMessage);
}
// Apply error states if validation errors are provided
if (response.data && response.data.errors) {
this.applyErrorStates(response.data.errors);
}
}
},
error: (xhr, status, error) => {
$(document).trigger('easy-invoice-save-failed', [xhr]);
// Restore button state
$saveBtn.html(originalBtnText);
$saveBtn.prop('disabled', false).removeAttr('aria-busy');
// Try to parse the response text as JSON
let errorMessage = 'An error occurred while saving the invoice';
try {
const jsonResponse = JSON.parse(xhr.responseText);
if (jsonResponse && jsonResponse.data && jsonResponse.data.message) {
errorMessage = jsonResponse.data.message;
}
} catch (e) {
// Could not parse error response as JSON
}
// Show error message
if (typeof EasyInvoiceToast !== 'undefined') {
EasyInvoiceToast.show('error', errorMessage);
}
}
});
},
// Function to collect form data
collectFormData: function() {
// Get all form data using jQuery's serializeArray
const formArray = $('#invoice-form').serializeArray();
const data = {};
// Convert form array to object - exclude individual items fields to prevent duplication
$.each(formArray, function(i, field) {
// Skip individual items fields (items[0][title], items[0][description], etc.)
// These will be collected separately
if (!field.name.match(/^items\[\d+\]\[/)) {
data[field.name] = field.value;
}
});
// Collect items data separately
const items = [];
const itemRows = $('.invoice-item');
itemRows.each(function(index) {
const itemData = {};
const itemFields = $(this).find('input, select, textarea');
itemFields.each(function() {
const fieldName = $(this).attr('name');
if (fieldName) {
// Extract the field name from items[index][field_name] format
const match = fieldName.match(/items\[(\d+)\]\[([^\]]+)\]/);
if (match) {
const fieldKey = match[2];
let fieldValue = $(this).val();
// Handle checkbox fields
if ($(this).attr('type') === 'checkbox') {
fieldValue = $(this).is(':checked') ? '1' : '0';
}
itemData[fieldKey] = fieldValue;
}
}
});
// Only add item if it has at least a title or description
if (itemData.title || itemData.description) {
items.push(itemData);
}
});
// Add items to form data
data.items = items;
return data;
},
// Function to initialize page for an existing invoice
loadInvoiceData: function() {
// Check if we have an invoice ID
var invoiceId = parseInt(easyInvoice.invoiceId);
// If we have an invoice ID, load its data
if (invoiceId > 0) {
// No need to make an AJAX call - data is already loaded in PHP and passed via wp_localize_script
// Initialize client data if available
if ($('#customer-name').val()) {
}
// Update the UI/preview
this.updatePreview();
} else {
// No invoice ID found
}
},
// Function to show notification
showNotification: function(type, message) {
// Deprecated: Use EasyInvoiceToast instead
},
// Update invoice preview
updatePreview: function() {
// Update invoice details
$('#preview-invoice-title').text($('#invoice-title').val() || 'Invoice Title');
$('#preview-invoice-description').text($('#invoice-description').val() || 'Invoice description will appear here.');
$('#preview-invoice-number').text($('#invoice-number').val() || '-');
$('#preview-invoice-date').text($('#invoice-date').val() || '-');
$('#preview-due-date').text($('#due-date').val() || '-');
// Update client details
$('#preview-customer-name').text($('#customer-name').val() || 'Client Name');
$('#preview-customer-email').text($('#customer-email').val() || 'client@example.com');
// Update notes & terms
$('#preview-notes').text($('#notes').val() || 'Thank you for your business!');
$('#preview-terms').text($('#terms').val() || 'Payment is due within 30 days.');
// Update tax rate display
$('#preview-tax-rate').text($('#tax-rate').val() || '0');
// Update template
const selectedTemplate = $('input[name="invoice_template"]:checked').val() || 'standard';
$('#preview-live').removeClass('template-standard template-professional template-minimal')
.addClass('template-' + selectedTemplate);
// If payment manager is available, let it update the totals
if (window.EasyInvoicePayment && typeof window.EasyInvoicePayment.updateTotals === 'function') {
window.EasyInvoicePayment.updateTotals();
}
},
// Reload client data after save
reloadClientData: function(clientId) {
if (!clientId) {
return;
}
// Update the client ID field
$('#client-id').val(clientId);
// Update the select dropdown
$('#select-client').val(clientId);
// Check if we have client data already loaded from PHP
if (typeof easyInvoice !== 'undefined' && easyInvoice.clientData && easyInvoice.clientData.id == clientId) {
const client = easyInvoice.clientData;
// Update client display fields
const clientName = client.name || 'N/A';
$('#selected-client-name').text(clientName);
$('#selected-client-email').text(client.email || 'N/A');
$('#display-client-name').text(clientName);
$('#display-client-company').text(client.company || 'N/A');
$('#display-client-email').text(client.email || 'N/A');
$('#display-client-phone').text(client.phone || 'N/A');
$('#display-client-website').text(client.website || 'N/A');
$('#display-client-address').text(client.address || 'N/A');
// 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=' + clientId);
// Update search input
$('#client-search-input').val(clientName);
} else {
// If client data is not available from PHP, we need to reload the page
// to get the updated client data from the server
window.location.reload();
}
}
};
// Initialize invoice save when document is ready
$(document).ready(function() {
window.EasyInvoiceSave.init();
});
})(jQuery);