271 lines
10 KiB
JavaScript
271 lines
10 KiB
JavaScript
(function () {
|
||
const actionMap = {
|
||
'new-ppr': "openNewPPRModal",
|
||
'book-out': "openLocalFlightModal",
|
||
'book-in': "openBookInModal",
|
||
overflight: "openOverflightModal",
|
||
'user-aircraft': "openUserAircraftModal",
|
||
'user-management': "openUserManagementModal"
|
||
};
|
||
|
||
function injectTopbarStyles() {
|
||
if (document.getElementById('shared-topbar-styles')) return;
|
||
|
||
const style = document.createElement('style');
|
||
style.id = 'shared-topbar-styles';
|
||
style.textContent = `
|
||
.top-bar {
|
||
background: linear-gradient(135deg, #2c3e50, #3498db);
|
||
color: white;
|
||
padding: 0.5rem 2rem;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
gap: 2rem;
|
||
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
|
||
position: relative;
|
||
z-index: 100;
|
||
}
|
||
.top-bar .title { order: 2; flex: 1; text-align: center; }
|
||
.top-bar .title h1 { margin: 0; font-size: 1.5rem; }
|
||
.top-bar .menu-buttons {
|
||
order: 1;
|
||
display: flex;
|
||
gap: 1rem;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
}
|
||
.top-bar .user-info {
|
||
order: 3;
|
||
font-size: 0.9rem;
|
||
opacity: 0.9;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.3rem;
|
||
}
|
||
.top-bar .btn {
|
||
padding: 0.7rem 1.5rem;
|
||
border: none;
|
||
border-radius: 5px;
|
||
cursor: pointer;
|
||
font-size: 0.9rem;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
display: inline-block;
|
||
}
|
||
.top-bar .btn-success { background-color: #27ae60; color: white; }
|
||
.top-bar .btn-warning { background-color: #f39c12; color: white; }
|
||
.dropdown { position: relative; display: inline-block; }
|
||
.dropdown-toggle { white-space: nowrap; }
|
||
.dropdown-menu {
|
||
display: none;
|
||
position: absolute;
|
||
background-color: white;
|
||
min-width: 220px;
|
||
box-shadow: 0 8px 16px rgba(0,0,0,0.2);
|
||
border-radius: 5px;
|
||
z-index: 1000;
|
||
top: 100%;
|
||
left: 0;
|
||
margin-top: 0.5rem;
|
||
padding: 0;
|
||
}
|
||
.dropdown-menu.active { display: block; }
|
||
.dropdown-menu a {
|
||
color: #333;
|
||
padding: 0.75rem 1.2rem;
|
||
text-decoration: none;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
gap: 1rem;
|
||
transition: background-color 0.2s ease;
|
||
white-space: nowrap;
|
||
}
|
||
.dropdown-menu a:hover { background-color: #f5f5f5; }
|
||
.dropdown-menu a:first-child { border-radius: 5px 5px 0 0; }
|
||
.dropdown-menu a:last-child { border-radius: 0 0 5px 5px; }
|
||
.shortcut { font-size: 0.8rem; color: #999; margin-left: 1rem; }
|
||
`;
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
function actionLink(label, action, shortcut, id = '') {
|
||
const shortcutText = shortcut ? `<span class="shortcut">(${shortcut})</span>` : '';
|
||
const idAttr = id ? ` id="${id}"` : '';
|
||
const hidden = id === 'user-management-dropdown' ? ' style="display: none;"' : '';
|
||
return `<a href="#" data-topbar-action="${action}"${idAttr}${hidden}>${label} ${shortcutText}</a>`;
|
||
}
|
||
|
||
function navLink(label, href) {
|
||
return `<a href="${href}">${label}</a>`;
|
||
}
|
||
|
||
function normalizeTopbar() {
|
||
const topbar = document.querySelector('.top-bar');
|
||
if (!topbar || topbar.dataset.sharedTopbar === 'true') return;
|
||
|
||
injectTopbarStyles();
|
||
|
||
const existingTitle = topbar.querySelector('#tower-title, .title h1, h1');
|
||
const path = window.location.pathname.replace(/\/$/, '') || '/';
|
||
const titleByPath = {
|
||
'/admin': '✈️ Swansea Tower',
|
||
'/atc': '✈️ Swansea Tower - ATC View',
|
||
'/reports': '📊 PPR Reports',
|
||
'/movements': '📈 Movements',
|
||
'/drone-requests': 'Drone Requests',
|
||
'/bulk-log': '🧾 Bulk Flight Log',
|
||
'/journal': '📔 Journal Log'
|
||
};
|
||
const titleText = titleByPath[path] || (existingTitle ? existingTitle.textContent.trim() : 'Tower Ops');
|
||
const existingUser = topbar.querySelector('#current-user');
|
||
const username = existingUser ? existingUser.textContent.trim() : 'Loading...';
|
||
|
||
topbar.dataset.sharedTopbar = 'true';
|
||
topbar.innerHTML = `
|
||
<div class="title">
|
||
<h1 id="tower-title">${titleText}</h1>
|
||
</div>
|
||
<div class="menu-buttons">
|
||
<div class="dropdown">
|
||
<button class="btn btn-success dropdown-toggle" id="actionsDropdownBtn">📋 Actions</button>
|
||
<div class="dropdown-menu" id="actionsDropdownMenu">
|
||
${actionLink('➕ New PPR', 'new-ppr', 'N')}
|
||
${actionLink('🛫 Book Out', 'book-out', 'L')}
|
||
${actionLink('🛬 Book In', 'book-in', 'I')}
|
||
${actionLink('🔄 Overflight', 'overflight', 'O')}
|
||
</div>
|
||
</div>
|
||
<div class="dropdown">
|
||
<button class="btn btn-warning dropdown-toggle" id="adminDropdownBtn">⚙️ Menu</button>
|
||
<div class="dropdown-menu" id="adminDropdownMenu">
|
||
${navLink('🏠 Admin View', '/admin')}
|
||
${navLink('🎛️ ATC View', '/atc')}
|
||
${navLink('📊 Reports', '/reports')}
|
||
${navLink('🛸 Drone Requests', '/drone-requests')}
|
||
${navLink('📔 Journal Log', '/journal')}
|
||
${actionLink('✈️ User Aircraft', 'user-aircraft', '', 'user-aircraft-dropdown')}
|
||
${actionLink('👥 User Management', 'user-management', '', 'user-management-dropdown')}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="user-info">
|
||
Logged in as: <span id="current-user">${username || 'Loading...'}</span> |
|
||
<a href="#" data-topbar-logout style="color: white;">Logout</a>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function closeDropdowns(except = null) {
|
||
document.querySelectorAll('.dropdown-menu.active').forEach(menu => {
|
||
if (menu !== except) menu.classList.remove('active');
|
||
});
|
||
}
|
||
|
||
function runAction(action) {
|
||
const fnName = actionMap[action];
|
||
if (fnName && typeof window[fnName] === 'function') {
|
||
if (action === 'book-out') {
|
||
window[fnName]('LOCAL');
|
||
} else {
|
||
window[fnName]();
|
||
}
|
||
return;
|
||
}
|
||
|
||
window.location.href = `/admin?action=${encodeURIComponent(action)}`;
|
||
}
|
||
|
||
function handleDeferredAction() {
|
||
const params = new URLSearchParams(window.location.search);
|
||
const action = params.get('action');
|
||
if (!action) return;
|
||
|
||
window.setTimeout(() => {
|
||
runAction(action);
|
||
const cleanUrl = window.location.pathname + window.location.hash;
|
||
window.history.replaceState({}, document.title, cleanUrl);
|
||
}, 300);
|
||
}
|
||
|
||
async function updateRoleVisibility() {
|
||
const token = localStorage.getItem('ppr_access_token');
|
||
const userManagement = document.getElementById('user-management-dropdown');
|
||
const userAircraft = document.getElementById('user-aircraft-dropdown');
|
||
|
||
if (!token || (!userManagement && !userAircraft)) return;
|
||
|
||
try {
|
||
const response = await fetch('/api/v1/auth/test-token', {
|
||
method: 'POST',
|
||
headers: { 'Authorization': `Bearer ${token}` }
|
||
});
|
||
if (!response.ok) return;
|
||
|
||
const user = await response.json();
|
||
const role = (user.role || '').toUpperCase();
|
||
if (role === 'ADMINISTRATOR') {
|
||
if (userManagement) userManagement.style.display = 'flex';
|
||
if (userAircraft) userAircraft.style.display = 'flex';
|
||
} else if (role === 'OPERATOR') {
|
||
if (userManagement) userManagement.style.display = 'none';
|
||
if (userAircraft) userAircraft.style.display = 'flex';
|
||
} else {
|
||
if (userManagement) userManagement.style.display = 'none';
|
||
if (userAircraft) userAircraft.style.display = 'none';
|
||
}
|
||
} catch (error) {
|
||
if (userManagement) userManagement.style.display = 'none';
|
||
}
|
||
}
|
||
|
||
document.addEventListener('click', event => {
|
||
const toggle = event.target.closest('.dropdown-toggle');
|
||
const action = event.target.closest('[data-topbar-action]');
|
||
const logout = event.target.closest('[data-topbar-logout]');
|
||
|
||
if (toggle) {
|
||
event.preventDefault();
|
||
event.stopImmediatePropagation();
|
||
const menu = toggle.parentElement.querySelector('.dropdown-menu');
|
||
const willOpen = !menu.classList.contains('active');
|
||
closeDropdowns(menu);
|
||
menu.classList.toggle('active', willOpen);
|
||
return;
|
||
}
|
||
|
||
if (action) {
|
||
event.preventDefault();
|
||
event.stopImmediatePropagation();
|
||
closeDropdowns();
|
||
runAction(action.dataset.topbarAction);
|
||
return;
|
||
}
|
||
|
||
if (logout) {
|
||
event.preventDefault();
|
||
event.stopImmediatePropagation();
|
||
if (typeof window.logout === 'function') {
|
||
window.logout();
|
||
} else {
|
||
localStorage.removeItem('ppr_access_token');
|
||
localStorage.removeItem('ppr_username');
|
||
localStorage.removeItem('ppr_token_expiry');
|
||
window.location.href = '/admin';
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!event.target.closest('.dropdown')) {
|
||
closeDropdowns();
|
||
}
|
||
});
|
||
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
normalizeTopbar();
|
||
updateRoleVisibility();
|
||
handleDeferredAction();
|
||
});
|
||
})();
|