PluginProbe
Easy Invoice – Invoice Generator, PDF Quotes & Payments / 2.3.6
Easy Invoice – Invoice Generator, PDF Quotes & Payments v2.3.6
2.4.0 2.4.1 2.3.8 2.3.7 2.3.6 2.3.5 2.3.4 2.3.3 2.3.2 2.3.1 2.2.0 2.1.21 2.1.20 2.1.19 2.1.18 2.1.0 2.1.1 2.1.10 2.1.11 2.1.12 2.1.13 2.1.14 2.1.15 2.1.16 2.1.2 All 57 releases
easy-invoice / assets / js / easy-invoice-toast.js

easy-invoice-toast.js in Easy Invoice – Invoice Generator, PDF Quotes & Payments 2.3.6, at assets/js/easy-invoice-toast.js

295 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Easy Invoice Global Toast Notification System
3 *
4 * A consistent toast notification system for the entire Easy Invoice plugin.
5 * Replaces all alert() calls and inconsistent notification styles.
6 */
7
8 (function($) {
9 'use strict';
10
11 // Toast configuration
12 const TOAST_CONFIG = {
13 duration: 5000, // 5 seconds
14 position: 'top-right',
15 animation: 'slide-in',
16 maxToasts: 5
17 };
18
19 // Toast types and their styling
20 const TOAST_TYPES = {
21 success: {
22 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>',
23 bgColor: 'bg-green-50',
24 textColor: 'text-green-800',
25 borderColor: 'border-green-200',
26 iconColor: 'text-green-400'
27 },
28 error: {
29 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>',
30 bgColor: 'bg-red-50',
31 textColor: 'text-red-800',
32 borderColor: 'border-red-200',
33 iconColor: 'text-red-400'
34 },
35 warning: {
36 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>',
37 bgColor: 'bg-yellow-50',
38 textColor: 'text-yellow-800',
39 borderColor: 'border-yellow-200',
40 iconColor: 'text-yellow-400'
41 },
42 info: {
43 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>',
44 bgColor: 'bg-blue-50',
45 textColor: 'text-blue-800',
46 borderColor: 'border-blue-200',
47 iconColor: 'text-blue-400'
48 }
49 };
50
51 // Global toast container
52 let toastContainer = null;
53 let activeToasts = [];
54
55 /**
56 * Initialize the toast system
57 */
58 function init() {
59 createToastContainer();
60 $(document).ready(function() {
61 // Auto-hide toasts after duration
62 setInterval(function() {
63 activeToasts.forEach(function(toast, index) {
64 if (toast.autoHide && Date.now() - toast.created > TOAST_CONFIG.duration) {
65 hideToast(toast.id);
66 }
67 });
68 }, 1000);
69 });
70 }
71
72 /**
73 * Create the toast container
74 */
75 function createToastContainer() {
76 if (toastContainer) return;
77
78 toastContainer = $('<div>', {
79 id: 'easy-invoice-toast-container',
80 class: 'fixed top-4 right-4 z-50 space-y-2 max-w-sm w-full'
81 });
82
83 $('body').append(toastContainer);
84 }
85
86 /**
87 * Show a toast notification
88 *
89 * @param {string} type - success, error, warning, info
90 * @param {string} message - The message to display
91 * @param {object} options - Additional options
92 */
93 function show(type, message, options = {}) {
94 const config = {
95 duration: options.duration || TOAST_CONFIG.duration,
96 autoHide: options.autoHide !== false,
97 dismissible: options.dismissible !== false,
98 ...options
99 };
100
101 // Validate type
102 if (!TOAST_TYPES[type]) {
103 type = 'info';
104 }
105
106 const toastId = 'toast-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
107 const toastType = TOAST_TYPES[type];
108
109 // Create toast element
110 const toast = $('<div>', {
111 id: toastId,
112 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`,
113 html: `
114 <div class="flex items-start">
115 <div class="flex-shrink-0">
116 <div class="${toastType.iconColor}">${toastType.icon}</div>
117 </div>
118 <div class="ml-3 flex-1">
119 <p class="text-sm font-medium ${toastType.textColor}">${message}</p>
120 </div>
121 ${config.dismissible ? `
122 <div class="ml-4 flex-shrink-0">
123 <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">
124 <span class="sr-only">Close</span>
125 <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
126 <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" />
127 </svg>
128 </button>
129 </div>
130 ` : ''}
131 </div>
132 `
133 });
134
135 // Add to container
136 toastContainer.append(toast);
137
138 // Store toast info
139 const toastInfo = {
140 id: toastId,
141 element: toast,
142 type: type,
143 message: message,
144 created: Date.now(),
145 autoHide: config.autoHide,
146 duration: config.duration
147 };
148
149 activeToasts.push(toastInfo);
150
151 // Limit number of toasts
152 if (activeToasts.length > TOAST_CONFIG.maxToasts) {
153 const oldestToast = activeToasts.shift();
154 hideToast(oldestToast.id);
155 }
156
157 // Animate in
158 setTimeout(function() {
159 toast.removeClass('translate-x-full opacity-0').addClass('translate-x-0 opacity-100');
160 }, 10);
161
162 // Handle close button
163 if (config.dismissible) {
164 toast.find('.toast-close').on('click', function() {
165 hideToast(toastId);
166 });
167 }
168
169 // Auto-hide if enabled
170 if (config.autoHide && config.duration > 0) {
171 setTimeout(function() {
172 hideToast(toastId);
173 }, config.duration);
174 }
175
176 return toastId;
177 }
178
179 /**
180 * Hide a specific toast
181 *
182 * @param {string} toastId - The ID of the toast to hide
183 */
184 function hideToast(toastId) {
185 const toastIndex = activeToasts.findIndex(t => t.id === toastId);
186 if (toastIndex === -1) return;
187
188 const toast = activeToasts[toastIndex];
189
190 // Animate out
191 toast.element.addClass('translate-x-full opacity-0');
192
193 setTimeout(function() {
194 toast.element.remove();
195 activeToasts.splice(toastIndex, 1);
196 }, 300);
197 }
198
199 /**
200 * Hide all toasts
201 */
202 function hideAll() {
203 activeToasts.forEach(function(toast) {
204 hideToast(toast.id);
205 });
206 }
207
208 /**
209 * Show success toast
210 */
211 function success(message, options = {}) {
212 return show('success', message, options);
213 }
214
215 /**
216 * Show error toast
217 */
218 function error(message, options = {}) {
219 return show('error', message, options);
220 }
221
222 /**
223 * Show warning toast
224 */
225 function warning(message, options = {}) {
226 return show('warning', message, options);
227 }
228
229 /**
230 * Show info toast
231 */
232 function info(message, options = {}) {
233 return show('info', message, options);
234 }
235
236 // Initialize when DOM is ready
237 $(document).ready(function() {
238 init();
239 });
240
241 // Expose the toast API globally
242 window.EasyInvoiceToast = {
243 show: show,
244 success: success,
245 error: error,
246 warning: warning,
247 info: info,
248 hide: hideToast,
249 hideAll: hideAll
250 };
251
252 // Temporarily disable alert override to prevent errors
253 // window.originalAlert = window.alert;
254 // window.alert = function(message) {
255 // EasyInvoiceToast.info(message, { duration: 4000 });
256 // };
257
258 // Global AJAX response handler for toast notifications
259 $(document).ajaxSuccess(function(event, xhr, settings) {
260 try {
261 const response = JSON.parse(xhr.responseText);
262
263 // Check if response contains toast data
264 if (response.data && response.data.toast) {
265 const toast = response.data.toast;
266 const options = toast.options || {};
267
268 switch (toast.type) {
269 case 'success':
270 EasyInvoiceToast.success(toast.message, options);
271 break;
272 case 'error':
273 EasyInvoiceToast.error(toast.message, options);
274 break;
275 case 'warning':
276 EasyInvoiceToast.warning(toast.message, options);
277 break;
278 case 'info':
279 EasyInvoiceToast.info(toast.message, options);
280 break;
281 }
282 }
283 } catch (e) {
284 // Response is not JSON or doesn't contain toast data
285 }
286 });
287
288 $(document).ajaxError(function(event, xhr, settings, error) {
289 // Show error toast for failed AJAX requests
290 if (xhr.status !== 0) { // Don't show for aborted requests
291 EasyInvoiceToast.error('Request failed. Please try again.', { duration: 5000 });
292 }
293 });
294
295 })(jQuery);