# easy-invoice/2.1.21/assets/js/easy-invoice-toast.js

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.1.21. 295 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.1.21/code/assets/js/easy-invoice-toast.js
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.1.21/raw/assets/js/easy-invoice-toast.js
- Modified: 2025-08-14T09:51:16+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.1.21/code/assets/js/easy-invoice-toast.js#L10-L20`.

```javascript
/**
 * Easy Invoice Global Toast Notification System
 * 
 * A consistent toast notification system for the entire Easy Invoice plugin.
 * Replaces all alert() calls and inconsistent notification styles.
 */

(function($) {
    'use strict';

    // Toast configuration
    const TOAST_CONFIG = {
        duration: 5000, // 5 seconds
        position: 'top-right',
        animation: 'slide-in',
        maxToasts: 5
    };

    // Toast types and their styling
    const TOAST_TYPES = {
        success: {
            icon: '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path></svg>',
            bgColor: 'bg-green-50',
            textColor: 'text-green-800',
            borderColor: 'border-green-200',
            iconColor: 'text-green-400'
        },
        error: {
            icon: '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"></path></svg>',
            bgColor: 'bg-red-50',
            textColor: 'text-red-800',
            borderColor: 'border-red-200',
            iconColor: 'text-red-400'
        },
        warning: {
            icon: '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path></svg>',
            bgColor: 'bg-yellow-50',
            textColor: 'text-yellow-800',
            borderColor: 'border-yellow-200',
            iconColor: 'text-yellow-400'
        },
        info: {
            icon: '<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"></path></svg>',
            bgColor: 'bg-blue-50',
            textColor: 'text-blue-800',
            borderColor: 'border-blue-200',
            iconColor: 'text-blue-400'
        }
    };

    // Global toast container
    let toastContainer = null;
    let activeToasts = [];

    /**
     * Initialize the toast system
     */
    function init() {
        createToastContainer();
        $(document).ready(function() {
            // Auto-hide toasts after duration
            setInterval(function() {
                activeToasts.forEach(function(toast, index) {
                    if (toast.autoHide && Date.now() - toast.created > TOAST_CONFIG.duration) {
                        hideToast(toast.id);
                    }
                });
            }, 1000);
        });
    }

    /**
     * Create the toast container
     */
    function createToastContainer() {
        if (toastContainer) return;

        toastContainer = $('<div>', {
            id: 'easy-invoice-toast-container',
            class: 'fixed top-4 right-4 z-50 space-y-2 max-w-sm w-full'
        });

        $('body').append(toastContainer);
    }

    /**
     * Show a toast notification
     * 
     * @param {string} type - success, error, warning, info
     * @param {string} message - The message to display
     * @param {object} options - Additional options
     */
    function show(type, message, options = {}) {
        const config = {
            duration: options.duration || TOAST_CONFIG.duration,
            autoHide: options.autoHide !== false,
            dismissible: options.dismissible !== false,
            ...options
        };

        // Validate type
        if (!TOAST_TYPES[type]) {
            type = 'info';
        }

        const toastId = 'toast-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
        const toastType = TOAST_TYPES[type];

        // Create toast element
        const toast = $('<div>', {
            id: toastId,
            class: `easy-invoice-toast ${toastType.bgColor} ${toastType.borderColor} border rounded-lg p-4 shadow-lg transform transition-all duration-300 ease-in-out translate-x-full opacity-0`,
            html: `
                <div class="flex items-start">
                    <div class="flex-shrink-0">
                        <div class="${toastType.iconColor}">${toastType.icon}</div>
                    </div>
                    <div class="ml-3 flex-1">
                        <p class="text-sm font-medium ${toastType.textColor}">${message}</p>
                    </div>
                    ${config.dismissible ? `
                        <div class="ml-4 flex-shrink-0">
                            <button type="button" class="toast-close ${toastType.textColor} hover:${toastType.textColor.replace('text-', 'bg-').replace('-800', '-100')} rounded-md inline-flex focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-${type}-50 focus:ring-${type}-600">
                                <span class="sr-only">Close</span>
                                <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                                    <path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd" />
                                </svg>
                            </button>
                        </div>
                    ` : ''}
                </div>
            `
        });

        // Add to container
        toastContainer.append(toast);

        // Store toast info
        const toastInfo = {
            id: toastId,
            element: toast,
            type: type,
            message: message,
            created: Date.now(),
            autoHide: config.autoHide,
            duration: config.duration
        };

        activeToasts.push(toastInfo);

        // Limit number of toasts
        if (activeToasts.length > TOAST_CONFIG.maxToasts) {
            const oldestToast = activeToasts.shift();
            hideToast(oldestToast.id);
        }

        // Animate in
        setTimeout(function() {
            toast.removeClass('translate-x-full opacity-0').addClass('translate-x-0 opacity-100');
        }, 10);

        // Handle close button
        if (config.dismissible) {
            toast.find('.toast-close').on('click', function() {
                hideToast(toastId);
            });
        }

        // Auto-hide if enabled
        if (config.autoHide && config.duration > 0) {
            setTimeout(function() {
                hideToast(toastId);
            }, config.duration);
        }

        return toastId;
    }

    /**
     * Hide a specific toast
     * 
     * @param {string} toastId - The ID of the toast to hide
     */
    function hideToast(toastId) {
        const toastIndex = activeToasts.findIndex(t => t.id === toastId);
        if (toastIndex === -1) return;

        const toast = activeToasts[toastIndex];
        
        // Animate out
        toast.element.addClass('translate-x-full opacity-0');
        
        setTimeout(function() {
            toast.element.remove();
            activeToasts.splice(toastIndex, 1);
        }, 300);
    }

    /**
     * Hide all toasts
     */
    function hideAll() {
        activeToasts.forEach(function(toast) {
            hideToast(toast.id);
        });
    }

    /**
     * Show success toast
     */
    function success(message, options = {}) {
        return show('success', message, options);
    }

    /**
     * Show error toast
     */
    function error(message, options = {}) {
        return show('error', message, options);
    }

    /**
     * Show warning toast
     */
    function warning(message, options = {}) {
        return show('warning', message, options);
    }

    /**
     * Show info toast
     */
    function info(message, options = {}) {
        return show('info', message, options);
    }

    // Initialize when DOM is ready
    $(document).ready(function() {
        init();
    });

    // Expose the toast API globally
    window.EasyInvoiceToast = {
        show: show,
        success: success,
        error: error,
        warning: warning,
        info: info,
        hide: hideToast,
        hideAll: hideAll
    };

    // Temporarily disable alert override to prevent errors
    // window.originalAlert = window.alert;
    // window.alert = function(message) {
    //     EasyInvoiceToast.info(message, { duration: 4000 });
    // };

    // Global AJAX response handler for toast notifications
    $(document).ajaxSuccess(function(event, xhr, settings) {
        try {
            const response = JSON.parse(xhr.responseText);
            
            // Check if response contains toast data
            if (response.data && response.data.toast) {
                const toast = response.data.toast;
                const options = toast.options || {};
                
                switch (toast.type) {
                    case 'success':
                        EasyInvoiceToast.success(toast.message, options);
                        break;
                    case 'error':
                        EasyInvoiceToast.error(toast.message, options);
                        break;
                    case 'warning':
                        EasyInvoiceToast.warning(toast.message, options);
                        break;
                    case 'info':
                        EasyInvoiceToast.info(toast.message, options);
                        break;
                }
            }
        } catch (e) {
            // Response is not JSON or doesn't contain toast data
        }
    });

    $(document).ajaxError(function(event, xhr, settings, error) {
        // Show error toast for failed AJAX requests
        if (xhr.status !== 0) { // Don't show for aborted requests
            EasyInvoiceToast.error('Request failed. Please try again.', { duration: 5000 });
        }
    });

})(jQuery); 
```
