diff --git a/static/js/followup-action-handler.js b/static/js/followup-action-handler.js index 40263b3..0263e4f 100644 --- a/static/js/followup-action-handler.js +++ b/static/js/followup-action-handler.js @@ -105,7 +105,7 @@ class FollowUpActionHandler { form.dataset.submitted = 'true'; // Show success message - this.showToast('Follow-up action saved successfully!', 'success'); + UIUtils.showToast('Success', 'Follow-up action saved successfully!', 'success'); // Close modal after a short delay setTimeout(() => { @@ -137,10 +137,10 @@ class FollowUpActionHandler { errorMessage = response.message; } - this.showToast(errorMessage, 'danger'); + UIUtils.showToast('Error', errorMessage, 'danger'); } catch (e) { console.error('Error parsing error response:', e); - this.showToast('An unexpected error occurred. Please try again.', 'danger'); + UIUtils.showToast('Error', 'An unexpected error occurred. Please try again.', 'danger'); } } @@ -155,49 +155,6 @@ class FollowUpActionHandler { } } - showToast(message, type = 'success') { - const toastContainer = document.getElementById('toast-container') || this.createToastContainer(); - - // Create toast element - const toast = document.createElement('div'); - toast.className = `toast show bg-${type} text-white mb-2`; - toast.role = 'alert'; - toast.setAttribute('aria-live', 'assertive'); - toast.setAttribute('aria-atomic', 'true'); - - // Add toast content - toast.innerHTML = ` -
- ${type === 'success' ? 'Success' : 'Error'} - -
-
- ${message} -
- `; - - // Add to container - toastContainer.appendChild(toast); - - // Auto-remove after delay - setTimeout(() => { - toast.classList.remove('show'); - setTimeout(() => toast.remove(), 150); - }, 5000); - - // Initialize Bootstrap toast - const bsToast = new bootstrap.Toast(toast, { autohide: true, delay: 5000 }); - bsToast.show(); - } - - createToastContainer() { - const container = document.createElement('div'); - container.id = 'toast-container'; - container.className = 'position-fixed bottom-0 end-0 p-3'; - container.style.zIndex = '1100'; - document.body.appendChild(container); - return container; - } } // Initialize the handler when the DOM is fully loaded diff --git a/static/js/main.js b/static/js/main.js index f1abe53..5c4953b 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -37,8 +37,7 @@ function initCSRFHandling() { */ function initUIComponents() { // Bootstrap component initialization - const tooltips = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); - tooltips.forEach(tooltip => new bootstrap.Tooltip(tooltip)); + UIUtils.initTooltips(); const popovers = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]')); popovers.forEach(popover => new bootstrap.Popover(popover)); @@ -96,13 +95,13 @@ function initFormHandling() { if (data.redirect) { window.location.href = data.redirect; } else if (data.message) { - showToast('Success', data.message, 'success'); + UIUtils.showToast('Success', data.message, 'success'); } } else { handleFormErrors(form, await response.json()); } } catch (error) { - showToast('Error', 'An unexpected error occurred', 'danger'); + UIUtils.showToast('Error', 'An unexpected error occurred', 'danger'); } finally { submitBtn.disabled = false; } @@ -130,7 +129,7 @@ function initErrorHandling() { const now = Date.now(); const errorMsg = `${message} @ ${source}:${lineno}`; if (isDev || (errorMsg !== lastErrorMsg || now - lastErrorTime > ERROR_TOAST_SUPPRESS_MS)) { - showToast('Application Error', 'An unexpected error occurred', 'danger'); + UIUtils.showToast('Application Error', 'An unexpected error occurred', 'danger'); lastErrorMsg = errorMsg; lastErrorTime = now; } @@ -140,7 +139,7 @@ function initErrorHandling() { window.addEventListener('unhandledrejection', event => { console.error('Unhandled rejection:', event.reason); if (isDev || (event.reason && event.reason.message !== lastErrorMsg)) { - showToast('Request Failed', event.reason && event.reason.message ? event.reason.message : 'Action failed', 'danger'); + UIUtils.showToast('Request Failed', event.reason && event.reason.message ? event.reason.message : 'Action failed', 'danger'); lastErrorMsg = event.reason && event.reason.message ? event.reason.message : ''; lastErrorTime = Date.now(); } @@ -172,31 +171,6 @@ function initAnalytics() { /** * UI Utilities */ -function showToast(title, message, variant = 'success') { - const toastContainer = document.getElementById('toast-container') || createToastContainer(); - const toast = document.createElement('div'); - - toast.className = `toast align-items-center text-bg-${variant} border-0`; - toast.innerHTML = ` -
-
- ${title}
${message} -
- -
- `; - - toastContainer.appendChild(toast); - new bootstrap.Toast(toast, { autohide: true, delay: 5000 }).show(); -} - -function createToastContainer() { - const container = document.createElement('div'); - container.id = 'toast-container'; - container.className = 'toast-container position-fixed bottom-0 end-0 p-3'; - document.body.appendChild(container); - return container; -} /** * Security Utilities @@ -286,19 +260,20 @@ async function loadDynamicContent(container, url) { */ document.addEventListener('ajax:success', (event) => { const [data, status, xhr] = event.detail; - showToast('Success', data.message || 'Action completed successfully', 'success'); + UIUtils.showToast('Success', data.message || 'Action completed successfully', 'success'); }); document.addEventListener('ajax:error', (event) => { const [error, status, xhr] = event.detail; - showToast('Error', error.message || 'Action failed', 'danger'); + UIUtils.showToast('Error', error.message || 'Action failed', 'danger'); }); // Export for module usage if needed if (typeof module !== 'undefined' && module.exports) { + const uiUtils = require('./ui-utils'); module.exports = { debounce, - showToast, + showToast: uiUtils.showToast, sanitizeInput }; } \ No newline at end of file diff --git a/static/js/public.js b/static/js/public.js index b1574c1..5138013 100644 --- a/static/js/public.js +++ b/static/js/public.js @@ -5,10 +5,7 @@ document.addEventListener('DOMContentLoaded', function() { // Initialize tooltips - const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); - tooltipTriggerList.map(function (tooltipTriggerEl) { - return new bootstrap.Tooltip(tooltipTriggerEl); - }); + UIUtils.initTooltips(); // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { diff --git a/static/js/ui-utils.js b/static/js/ui-utils.js new file mode 100644 index 0000000..858a1e8 --- /dev/null +++ b/static/js/ui-utils.js @@ -0,0 +1,38 @@ +(function(window) { + function createToastContainer() { + const container = document.createElement('div'); + container.id = 'toast-container'; + container.className = 'toast-container position-fixed bottom-0 end-0 p-3'; + container.style.zIndex = '1100'; + document.body.appendChild(container); + return container; + } + + function showToast(title, message, variant = 'success') { + const toastContainer = document.getElementById('toast-container') || createToastContainer(); + const toast = document.createElement('div'); + toast.className = `toast align-items-center text-bg-${variant} border-0`; + toast.innerHTML = ` +
+
+ ${title}${message ? `
${message}` : ''} +
+ +
+ `; + toastContainer.appendChild(toast); + new bootstrap.Toast(toast, { autohide: true, delay: 5000 }).show(); + } + + function initTooltips(container = document) { + const tooltipElements = [].slice.call(container.querySelectorAll('[data-bs-toggle="tooltip"]')); + tooltipElements.forEach(el => new bootstrap.Tooltip(el)); + } + + window.UIUtils = { + createToastContainer, + showToast, + initTooltips + }; + window.showToast = showToast; +})(window); diff --git a/templates/base.html b/templates/base.html index adec5f1..2396a5c 100644 --- a/templates/base.html +++ b/templates/base.html @@ -242,8 +242,6 @@ - -