diff --git a/map/app/public/css/modules/base.css b/map/app/public/css/modules/base.css
index aeb1ba8..2196bc3 100644
--- a/map/app/public/css/modules/base.css
+++ b/map/app/public/css/modules/base.css
@@ -38,7 +38,33 @@
*/
}
-/* Reset and base styles */
+/* Authentication loading states */
+body.authenticating {
+ overflow: hidden;
+ background: #ffffff; /* Clean white background during auth */
+}
+
+body.authenticating #app.app-hidden {
+ opacity: 0;
+ visibility: hidden;
+ transition: none; /* No transition while hiding */
+ transform: translateY(10px); /* Slight offset for smooth entrance */
+}
+
+body.authenticated #app {
+ opacity: 1;
+ visibility: visible;
+ transform: translateY(0);
+ transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s ease;
+}
+
+/* Enhanced loading overlay for authentication */
+body.authenticating .loading-overlay {
+ background-color: rgba(255, 255, 255, 0.98);
+ backdrop-filter: blur(2px);
+}
+
+/* Base styles */
* {
margin: 0;
padding: 0;
diff --git a/map/app/public/css/modules/notifications.css b/map/app/public/css/modules/notifications.css
index 8b27bbc..64309f0 100644
--- a/map/app/public/css/modules/notifications.css
+++ b/map/app/public/css/modules/notifications.css
@@ -63,7 +63,14 @@
flex-direction: column;
align-items: center;
justify-content: center;
- z-index: 12500;
+ z-index: 15000; /* Higher than everything else during auth */
+}
+
+/* Enhanced loading for authentication state */
+body.authenticating .loading-overlay {
+ background-color: rgba(255,255,255,0.98);
+ backdrop-filter: blur(2px);
+ z-index: 15000;
}
.loading-overlay.hidden {
@@ -88,3 +95,31 @@
color: var(--dark-color);
font-size: 16px;
}
+
+.loading-patience-message {
+ margin-top: 10px !important;
+ font-size: 14px !important;
+ color: #666 !important;
+ max-width: 320px;
+ text-align: center;
+ line-height: 1.4;
+ opacity: 0.85;
+ padding: 0 20px; /* Add padding for mobile */
+}
+
+/* Better spinner animation during auth */
+body.authenticating .spinner {
+ width: 60px;
+ height: 60px;
+ border: 4px solid var(--light-color);
+ border-top-color: var(--primary-color);
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+}
+
+/* Larger text during authentication */
+body.authenticating .loading-overlay p:first-of-type {
+ font-size: 18px;
+ font-weight: 500;
+ color: var(--primary-color);
+}
diff --git a/map/app/public/index.html b/map/app/public/index.html
index 323a63f..5515a5e 100644
--- a/map/app/public/index.html
+++ b/map/app/public/index.html
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/map/app/public/js/auth.js b/map/app/public/js/auth.js
index e4e031d..aeacdd7 100644
--- a/map/app/public/js/auth.js
+++ b/map/app/public/js/auth.js
@@ -11,26 +11,31 @@ export async function checkAuth() {
// Check if user has expired
if (data.expired) {
showStatus('Account has expired. Please contact an administrator.', 'error');
- setTimeout(() => {
- window.location.href = '/login.html?expired=true';
- }, 2000);
+ // Immediate redirect for expired users
+ window.location.href = '/login.html?expired=true';
throw new Error('Account expired');
}
if (!data.authenticated) {
+ // Immediate redirect for unauthenticated users
window.location.href = '/login.html';
throw new Error('Not authenticated');
}
currentUser = data.user;
currentUser.userType = data.user.userType || 'user'; // Ensure userType is set
+
+ // Authentication successful - show the app
+ document.body.classList.remove('authenticating');
+ document.body.classList.add('authenticated');
+ document.getElementById('app').classList.remove('app-hidden');
+
updateUserInterface();
} catch (error) {
console.error('Auth check failed:', error);
- if (error.message !== 'Account expired') {
- window.location.href = '/login.html';
- }
+ // Always redirect immediately on any auth failure
+ window.location.href = '/login.html';
throw error;
}
}
diff --git a/map/app/public/js/main.js b/map/app/public/js/main.js
index 0cbd0d4..3639d51 100644
--- a/map/app/public/js/main.js
+++ b/map/app/public/js/main.js
@@ -1,6 +1,6 @@
// Main application entry point
import { CONFIG, loadDomainConfig } from './config.js';
-import { hideLoading, showStatus, setViewportDimensions } from './utils.js';
+import { hideLoading, showStatus, setViewportDimensions, updateLoadingMessage } from './utils.js';
import { checkAuth } from './auth.js';
import { initializeMap, getMap } from './map-manager.js';
import { loadLocations } from './location-manager.js';
@@ -23,28 +23,36 @@ document.addEventListener('DOMContentLoaded', async () => {
setTimeout(setViewportDimensions, 100);
});
- console.log('DOM loaded, initializing application...');
+ console.log('DOM loaded, checking authentication...');
try {
// Load domain configuration first
+ updateLoadingMessage('Loading configuration...');
await loadDomainConfig();
- // First check authentication
+ // CRITICAL: Check authentication FIRST before any UI initialization
+ // This prevents the map from briefly showing before redirect
+ updateLoadingMessage('Authenticating...');
await checkAuth();
+ console.log('Authentication confirmed, initializing application...');
+
// Setup temp user security measures after authentication
setupTempUserSecurity();
// Then initialize the map
+ updateLoadingMessage('Initializing map...');
await initializeMap();
// Initialize cut manager after map is ready
+ updateLoadingMessage('Loading map overlays...');
await cutManager.initialize(getMap());
// Initialize cut controls for public map
await initializeCutControls();
// Only load locations after map is ready
+ updateLoadingMessage('Loading locations...');
await loadLocations();
// Setup other features
@@ -52,13 +60,24 @@ document.addEventListener('DOMContentLoaded', async () => {
setupAutoRefresh();
// Initialize Unified Search
+ updateLoadingMessage('Setting up search...');
await initializeUnifiedSearch();
} catch (error) {
console.error('Initialization error:', error);
- showStatus('Failed to initialize application', 'error');
+ // If authentication fails, we should already be redirected
+ // But in case of other errors, clean up the loading state
+ if (!error.message.includes('Not authenticated') && !error.message.includes('Account expired')) {
+ document.body.classList.remove('authenticating');
+ document.body.classList.add('authenticated');
+ document.getElementById('app').classList.remove('app-hidden');
+ showStatus('Failed to initialize application', 'error');
+ }
} finally {
- hideLoading();
+ // Only hide loading if we're not being redirected for authentication
+ if (!document.body.classList.contains('authenticating')) {
+ hideLoading();
+ }
}
});
diff --git a/map/app/public/js/utils.js b/map/app/public/js/utils.js
index 771a702..80f194d 100644
--- a/map/app/public/js/utils.js
+++ b/map/app/public/js/utils.js
@@ -52,6 +52,13 @@ export function hideLoading() {
}
}
+export function updateLoadingMessage(message) {
+ const loadingElement = document.querySelector('#loading p');
+ if (loadingElement) {
+ loadingElement.textContent = message;
+ }
+}
+
export function updateLocationCount(count) {
const countElement = document.getElementById('location-count');
const mobileCountElement = document.getElementById('mobile-location-count');
diff --git a/map/listmonk-env-example.txt b/map/listmonk-env-example.txt
deleted file mode 100644
index 09cada8..0000000
--- a/map/listmonk-env-example.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-# Add these Listmonk configuration variables to your .env file
-
-# Listmonk Configuration
-LISTMONK_API_URL=http://listmonk:9000/api
-LISTMONK_USERNAME=admin
-LISTMONK_PASSWORD=your-secure-listmonk-password
-LISTMONK_SYNC_ENABLED=true
-LISTMONK_INITIAL_SYNC=false # Set to true only for first run to sync existing data
-
-# Note: Make sure to:
-# 1. Replace 'your-secure-listmonk-password' with your actual Listmonk admin password
-# 2. Update the LISTMONK_API_URL if your Listmonk instance runs on a different host/port
-# 3. Set LISTMONK_INITIAL_SYNC=true only once to sync existing data, then set back to false
-# 4. Restart the Map application after updating these variables