(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; }
.notification-badge {
min-width: 1.4rem;
height: 1.4rem;
padding: 0 0.35rem;
border-radius: 999px;
background: #e74c3c;
color: white;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 700;
line-height: 1;
}
.notification-badge[hidden] { display: none; }
`;
document.head.appendChild(style);
}
function actionLink(label, action, shortcut, id = '') {
const shortcutText = shortcut ? `(${shortcut})` : '';
const idAttr = id ? ` id="${id}"` : '';
const hidden = id === 'user-management-dropdown' ? ' style="display: none;"' : '';
return `${label} ${shortcutText}`;
}
function navLink(label, href, id = '') {
const idAttr = id ? ` id="${id}"` : '';
return `${label}`;
}
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 = `
${titleText}
Logged in as:
${username || 'Loading...'} |
Logout
`;
}
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';
}
}
async function refreshDroneRequestBadge() {
const badges = [
document.getElementById('drone-request-badge'),
document.getElementById('drone-request-menu-badge'),
].filter(Boolean);
if (!badges.length) return;
const token = localStorage.getItem('ppr_access_token');
if (!token) {
badges.forEach(badge => {
badge.hidden = true;
});
return;
}
try {
const response = await fetch('/api/v1/drone-requests/?status=NEW&limit=100', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!response.ok) {
badges.forEach(badge => {
badge.hidden = true;
});
return;
}
const requests = await response.json();
const count = Array.isArray(requests) ? requests.length : 0;
const title = count === 1
? '1 drone request waiting for approval'
: `${count} drone requests waiting for approval`;
badges.forEach(badge => {
badge.textContent = count > 99 ? '99+' : String(count);
badge.hidden = count === 0;
badge.title = title;
});
} catch (error) {
badges.forEach(badge => {
badge.hidden = true;
});
}
}
window.refreshDroneRequestBadge = refreshDroneRequestBadge;
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();
refreshDroneRequestBadge();
handleDeferredAction();
window.setInterval(refreshDroneRequestBadge, 60000);
});
})();