Initial commit: Quality App v2 - FG Scan Module with Reports

This commit is contained in:
Quality App Developer
2026-01-25 22:25:18 +02:00
commit 3c5a273a89
66 changed files with 15368 additions and 0 deletions

137
app/static/js/base.js Normal file
View File

@@ -0,0 +1,137 @@
/* Base JavaScript Utilities */
document.addEventListener('DOMContentLoaded', function() {
// Initialize tooltips and popovers
initializeBootstrap();
// Setup flash message auto-close
setupFlashMessages();
// Setup theme toggle if available
setupThemeToggle();
});
/**
* Initialize Bootstrap tooltips and popovers
*/
function initializeBootstrap() {
// Initialize all tooltips
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl);
});
// Initialize all popovers
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'));
popoverTriggerList.map(function (popoverTriggerEl) {
return new bootstrap.Popover(popoverTriggerEl);
});
}
/**
* Auto-close flash messages after 5 seconds
*/
function setupFlashMessages() {
const alerts = document.querySelectorAll('.alert');
alerts.forEach(function(alert) {
setTimeout(function() {
const bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
}, 5000);
});
}
/**
* Setup theme toggle functionality
*/
function setupThemeToggle() {
const themeToggle = document.getElementById('theme-toggle');
if (!themeToggle) return;
const currentTheme = localStorage.getItem('theme') || 'light';
applyTheme(currentTheme);
themeToggle.addEventListener('click', function() {
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
applyTheme(newTheme);
localStorage.setItem('theme', newTheme);
});
}
/**
* Apply theme to document
*/
function applyTheme(theme) {
const html = document.documentElement;
if (theme === 'dark') {
html.setAttribute('data-bs-theme', 'dark');
} else {
html.removeAttribute('data-bs-theme');
}
}
/**
* Display a toast notification
*/
function showToast(message, type = 'info') {
const toastHtml = `
<div class="toast align-items-center text-white bg-${type} border-0" role="alert" aria-live="assertive" aria-atomic="true">
<div class="d-flex">
<div class="toast-body">
${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>
`;
const toastContainer = document.querySelector('.toast-container') || createToastContainer();
toastContainer.insertAdjacentHTML('beforeend', toastHtml);
const toastElement = toastContainer.lastElementChild;
const toast = new bootstrap.Toast(toastElement);
toast.show();
toastElement.addEventListener('hidden.bs.toast', function() {
toastElement.remove();
});
}
/**
* Create toast container if it doesn't exist
*/
function createToastContainer() {
const container = document.createElement('div');
container.className = 'toast-container position-fixed bottom-0 end-0 p-3';
document.body.appendChild(container);
return container;
}
/**
* Debounce function for input handlers
*/
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
/**
* Throttle function for scroll/resize handlers
*/
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}