This commit is contained in:
HotSwapp
2025-08-08 19:06:39 -05:00
parent b257a06787
commit 04edc636f8
12 changed files with 1824 additions and 52 deletions

View File

@@ -8,15 +8,51 @@
<!-- Bootstrap 5.3 CDN -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="/static/css/main.css" rel="stylesheet">
<link href="/static/css/themes.css" rel="stylesheet">
<link href="/static/css/components.css" rel="stylesheet">
<style>
/* Footer Enhancements */
footer .btn-outline-primary:hover {
background-color: #0d6efd;
border-color: #0d6efd;
color: white;
transform: translateY(-1px);
transition: all 0.2s ease;
}
footer .text-primary:hover {
color: #0056b3 !important;
transition: color 0.2s ease;
}
footer small {
color: #6c757d !important;
}
#currentPageDisplay {
color: #495057 !important;
font-weight: 500;
}
/* Responsive footer adjustments */
@media (max-width: 768px) {
footer .row {
text-align: center !important;
}
footer .col-md-6:first-child {
margin-bottom: 0.5rem;
}
}
</style>
{% block extra_head %}{% endblock %}
</head>
<body>
<body class="d-flex flex-column min-vh-100">
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
@@ -50,11 +86,6 @@
<i class="bi bi-file-text"></i> Documents <small>(Alt+D)</small>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/import" data-shortcut="Alt+I">
<i class="bi bi-upload"></i> Import <small>(Alt+I)</small>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/search" data-shortcut="Ctrl+F">
<i class="bi bi-search"></i> Search <small>(Ctrl+F)</small>
@@ -79,9 +110,37 @@
</nav>
<!-- Main Content -->
<div class="container-fluid mt-3">
{% block content %}{% endblock %}
</div>
<main class="flex-grow-1">
<div class="container-fluid mt-3 mb-4">
{% block content %}{% endblock %}
</div>
</main>
<!-- Footer -->
<footer class="mt-auto py-3 border-top shadow-sm" style="background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); border-color: #dee2e6 !important;">
<div class="container">
<div class="row align-items-center">
<div class="col-md-6">
<small class="text-muted">
&copy; 2024 Delphi Consulting Group Database System
<span class="mx-2">|</span>
<span id="currentPageDisplay">Loading...</span>
</small>
</div>
<div class="col-md-6 text-end">
<button type="button" class="btn btn-outline-primary btn-sm me-3" onclick="openSupportModal()">
<i class="fas fa-bug me-1"></i>Report Issue
</button>
<small class="text-muted">
Found a bug? <a href="#" onclick="openSupportModal()" class="text-primary text-decoration-none">Report Issue</a>
</small>
</div>
</div>
</div>
</footer>
<!-- Include Support Modal -->
{% include 'support_modal.html' %}
<!-- Keyboard Shortcuts Help Modal -->
<div class="modal fade" id="shortcutsModal" tabindex="-1" aria-labelledby="shortcutsModalLabel" aria-hidden="true">
@@ -158,13 +217,153 @@
// Initialize keyboard shortcuts on page load
document.addEventListener('DOMContentLoaded', function() {
initializeKeyboardShortcuts();
updateCurrentPageDisplay();
initializeAuthManager();
});
// Logout function
function logout() {
// Update current page display in footer
function updateCurrentPageDisplay() {
const path = window.location.pathname;
const pageNames = {
'/': 'Dashboard',
'/login': 'Login',
'/customers': 'Customer Management',
'/files': 'File Cabinet',
'/financial': 'Financial/Ledger',
'/documents': 'Document Management',
'/import': 'Data Import',
'/search': 'Advanced Search',
'/admin': 'System Administration'
};
const currentPage = pageNames[path] || `Page: ${path}`;
const displayElement = document.getElementById('currentPageDisplay');
if (displayElement) {
displayElement.textContent = `Current: ${currentPage}`;
}
}
// Authentication Manager
function initializeAuthManager() {
// Check if we have a valid token on page load
const token = localStorage.getItem('auth_token');
if (token && !isLoginPage()) {
// Verify token is still valid
checkTokenValidity();
// Set up periodic token refresh (every hour)
setInterval(refreshTokenIfNeeded, 3600000); // 1 hour
// Set up activity monitoring for auto-refresh
setupActivityMonitoring();
} else if (!isLoginPage() && !token) {
// No token and not on login page - redirect to login
window.location.href = '/login';
}
}
function isLoginPage() {
return window.location.pathname === '/login' || window.location.pathname === '/';
}
async function checkTokenValidity() {
const token = localStorage.getItem('auth_token');
if (!token) return false;
try {
const response = await fetch('/api/auth/me', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
// Token is invalid, remove it and redirect to login
localStorage.removeItem('auth_token');
if (!isLoginPage()) {
window.location.href = '/login';
}
return false;
}
return true;
} catch (error) {
console.error('Error checking token validity:', error);
return false;
}
}
async function refreshTokenIfNeeded() {
const token = localStorage.getItem('auth_token');
if (!token) return;
try {
// Try to get a new token
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('auth_token', data.access_token);
console.log('Token refreshed successfully');
} else {
// If refresh fails, check if current token is still valid
const isValid = await checkTokenValidity();
if (!isValid) {
localStorage.removeItem('auth_token');
window.location.href = '/login';
}
}
} catch (error) {
console.error('Error refreshing token:', error);
}
}
function setupActivityMonitoring() {
let lastActivity = Date.now();
// Track user activity
const activityEvents = ['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart'];
activityEvents.forEach(event => {
document.addEventListener(event, () => {
lastActivity = Date.now();
});
});
// Check every 30 minutes if user has been inactive for more than 4 hours
setInterval(() => {
const now = Date.now();
const fourHours = 4 * 60 * 60 * 1000;
if (now - lastActivity > fourHours) {
// User has been inactive for 4+ hours, logout
logout('Session expired due to inactivity');
}
}, 30 * 60 * 1000); // Check every 30 minutes
}
// Enhanced logout function
function logout(reason = null) {
localStorage.removeItem('auth_token');
if (reason) {
// Store logout reason to show on login page
sessionStorage.setItem('logout_reason', reason);
}
window.location.href = '/login';
}
// Make functions globally available
window.authManager = {
checkTokenValidity,
refreshTokenIfNeeded,
logout
};
</script>
</body>
</html>