| @@ -378,5 +378,113 @@ | ||
| 378 | 378 | // Update the visual state - ensure only the new template is selected |
| 379 | 379 | $mainGrid.find('.template-card').removeClass('selected'); |
| 380 | 380 | $mainGrid.find('.template-card[data-template-id="' + templateId + '"]').addClass('selected'); |
| 381 | 381 | } |
| 382 | + | |
| 383 | + // Save to Item Library functionality | |
| 384 | + $(document).on('click', '.save-to-library-btn', function(e) { | |
| 385 | + e.preventDefault(); | |
| 386 | + | |
| 387 | + var $btn = $(this); | |
| 388 | + var itemIndex = $btn.data('item-index'); | |
| 389 | + // Find the closest item container (either invoice-item or quote-item) | |
| 390 | + var $itemContainer = $btn.closest('.invoice-item, .quote-item'); | |
| 391 | + | |
| 392 | + if (!$itemContainer.length) { | |
| 393 | + return; | |
| 394 | + } | |
| 395 | + | |
| 396 | + // Get item data from the form | |
| 397 | + var itemData = { | |
| 398 | + title: '', | |
| 399 | + description: '', | |
| 400 | + price: 0, | |
| 401 | + quantity: 0, | |
| 402 | + taxable: 0 | |
| 403 | + }; | |
| 404 | + | |
| 405 | + // Extract data from the item fields | |
| 406 | + $itemContainer.find('input, textarea, select').each(function() { | |
| 407 | + var $field = $(this); | |
| 408 | + var name = $field.attr('name'); | |
| 409 | + var value = $field.val(); | |
| 410 | + var type = $field.attr('type'); | |
| 411 | + | |
| 412 | + if (name) { | |
| 413 | + // Handle array names like items[0][title] or quote_items[0][title] | |
| 414 | + var matches = name.match(/(items|quote_items)\[\d+\]\[(.+)\]/); | |
| 415 | + if (matches && matches[2]) { | |
| 416 | + if (type === 'checkbox' || type === 'radio') { | |
| 417 | + itemData[matches[2]] = $field.is(':checked') ? 1 : 0; | |
| 418 | + } else { | |
| 419 | + itemData[matches[2]] = value; | |
| 420 | + } | |
| 421 | + } | |
| 422 | + } | |
| 423 | + }); | |
| 424 | + | |
| 425 | + // Convert numeric fields | |
| 426 | + itemData.price = parseFloat(itemData.price) || 0; | |
| 427 | + itemData.quantity = parseInt(itemData.quantity) || 0; | |
| 428 | + itemData.taxable = parseInt(itemData.taxable) || 0; | |
| 429 | + | |
| 430 | + // Validate required fields | |
| 431 | + if (!itemData.title || !itemData.title.trim()) { | |
| 432 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 433 | + EasyInvoiceToast.error('Please fill in item title before saving to library'); | |
| 434 | + } else { | |
| 435 | + alert('Please fill in item title before saving to library'); | |
| 436 | + } | |
| 437 | + return; | |
| 438 | + } | |
| 439 | + | |
| 440 | + if (!itemData.price || itemData.price <= 0) { | |
| 441 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 442 | + EasyInvoiceToast.error('Please fill in item price before saving to library'); | |
| 443 | + } else { | |
| 444 | + alert('Please fill in item price before saving to library'); | |
| 445 | + } | |
| 446 | + return; | |
| 447 | + } | |
| 448 | + | |
| 449 | + // Show loading state | |
| 450 | + var originalHtml = $btn.html(); | |
| 451 | + $btn.html('<i class="fas fa-spinner fa-spin"></i>').prop('disabled', true); | |
| 452 | + | |
| 453 | + // AJAX call to save to item library | |
| 454 | + $.ajax({ | |
| 455 | + url: ajaxurl, | |
| 456 | + type: 'POST', | |
| 457 | + data: { | |
| 458 | + action: 'easy_invoice_pro_add_item', | |
| 459 | + nonce: $btn.data('nonce'), | |
| 460 | + item_data: JSON.stringify(itemData) | |
| 461 | + }, | |
| 462 | + success: function(response) { | |
| 463 | + if (response.success) { | |
| 464 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 465 | + EasyInvoiceToast.success('Item saved to library successfully!'); | |
| 466 | + } else { | |
| 467 | + alert('Item saved to library successfully!'); | |
| 468 | + } | |
| 469 | + } else { | |
| 470 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 471 | + EasyInvoiceToast.error(response.data.message || 'Failed to save item'); | |
| 472 | + } else { | |
| 473 | + alert('Failed to save item: ' + (response.data.message || 'Unknown error')); | |
| 474 | + } | |
| 475 | + } | |
| 476 | + }, | |
| 477 | + error: function() { | |
| 478 | + if (typeof EasyInvoiceToast !== 'undefined') { | |
| 479 | + EasyInvoiceToast.error('Error saving item to library'); | |
| 480 | + } else { | |
| 481 | + alert('Error saving item to library'); | |
| 482 | + } | |
| 483 | + }, | |
| 484 | + complete: function() { | |
| 485 | + // Restore button | |
| 486 | + $btn.html(originalHtml).prop('disabled', false); | |
| 487 | + } | |
| 488 | + }); | |
| 489 | + }); | |
| 382 | 490 | }); |