From c877fa01b4c004190cae0c5987cedc050d428264 Mon Sep 17 00:00:00 2001
From: Freddy Ouma <103921342+oumafreddy@users.noreply.github.com>
Date: Fri, 20 Jun 2025 16:35:48 +0300
Subject: [PATCH 1/3] Refactor UI helpers into shared module
---
static/js/followup-action-handler.js | 49 ++--------------------------
static/js/main.js | 43 +++++-------------------
static/js/public.js | 5 +--
static/js/ui-utils.js | 37 +++++++++++++++++++++
templates/base.html | 1 +
templates/public/base.html | 1 +
6 files changed, 52 insertions(+), 84 deletions(-)
create mode 100644 static/js/ui-utils.js
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 = `
-
-
- ${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..d7dfc15
--- /dev/null
+++ b/static/js/ui-utils.js
@@ -0,0 +1,37 @@
+(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);
diff --git a/templates/base.html b/templates/base.html
index adec5f1..c79473b 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -275,6 +275,7 @@ Legal
+
diff --git a/templates/public/base.html b/templates/public/base.html
index 10b853d..e5876b6 100644
--- a/templates/public/base.html
+++ b/templates/public/base.html
@@ -296,6 +296,7 @@ Contact Us
+
{% block extra_js %}{% endblock %}