v2
This commit is contained in:
@@ -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">
|
||||
© 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>
|
||||
Reference in New Issue
Block a user