Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 3 additions & 46 deletions static/js/followup-action-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand Down Expand Up @@ -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');
}
}

Expand All @@ -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 = `
<div class="toast-header bg-${type} text-white">
<strong class="me-auto">${type === 'success' ? 'Success' : 'Error'}</strong>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
${message}
</div>
`;

// 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
Expand Down
43 changes: 9 additions & 34 deletions static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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();
}
Expand Down Expand Up @@ -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 = `
<div class="d-flex">
<div class="toast-body">
<strong>${title}</strong><br>${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;

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
Expand Down Expand Up @@ -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
};
}
5 changes: 1 addition & 4 deletions static/js/public.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down
38 changes: 38 additions & 0 deletions static/js/ui-utils.js
Original file line number Diff line number Diff line change
@@ -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 = `
<div class="d-flex">
<div class="toast-body">
<strong>${title}</strong>${message ? `<br>${message}` : ''}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast"></button>
</div>
`;
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);
38 changes: 4 additions & 34 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@
</div>
</main>

<!-- Toast Container -->
<div id="toastContainer" class="toast-container position-fixed bottom-0 end-0 p-3" style="z-index: 1056"></div>

<!-- FOOTER -->
<footer>
Expand Down Expand Up @@ -275,6 +273,10 @@ <h5>Legal</h5>
<!-- Core Scripts -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="{% static 'js/ui-utils.js' %}"></script>
<script>
window.showToast = UIUtils.showToast;
</script>
<script src="{% static 'js/main.js' %}"></script>
<script src="{% static 'js/modal-handler.js' %}"></script>
<script src="{% static 'js/modal-context.js' %}"></script>
Expand Down Expand Up @@ -308,14 +310,6 @@ <h5>Legal</h5>
data-bs-toggle="tooltip" data-bs-placement="left" title="Ask Oreno AI Assistant about GRC best practices or platform features!">
<i class="bi bi-robot"></i>
</button>
<script>
document.addEventListener('DOMContentLoaded', function() {
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.forEach(function (tooltipTriggerEl) {
new bootstrap.Tooltip(tooltipTriggerEl);
});
});
</script>
{% endif %}

<!-- AI Assistant Modal -->
Expand Down Expand Up @@ -591,30 +585,6 @@ <h5 class="alert-heading">Error</h5>
});
});

// Enhanced toast function with better styling and auto-dismiss
window.showToast = function(message, type = 'success') {
var toastId = 'toast-' + Date.now();
var toastHtml = `
<div id="${toastId}" class="toast align-items-center text-white bg-${type} border-0 mb-2" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
<i class="bi ${type === 'success' ? 'bi-check-circle' : type === 'danger' ? 'bi-exclamation-circle' : 'bi-info-circle'} me-2"></i>
${message}
</div>
<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
</div>`;
$('#toast-container').append(toastHtml);
var toastEl = document.getElementById(toastId);
var toast = new bootstrap.Toast(toastEl, {
delay: 3500,
animation: true
});
toast.show();
toastEl.addEventListener('hidden.bs.toast', function() {
$(toastEl).remove();
});
};
})();
</script>

Expand Down
1 change: 1 addition & 0 deletions templates/public/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ <h3>Contact Us</h3>
<a href="#" id="backToTopBtn" class="back-to-top" aria-label="Back to top"><i class="fas fa-arrow-up"></i></a>

<!-- JavaScript -->
<script src="{% static 'js/ui-utils.js' %}"></script>
<script src="{% static 'js/public.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
Expand Down