AI tweaking

This commit is contained in:
2025-12-19 08:06:47 -05:00
parent 63564b54dd
commit 0149f45893

View File

@@ -421,7 +421,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2 id="local-flight-modal-title">Book Out</h2> <h2 id="local-flight-modal-title">Book Out</h2>
<button class="close" onclick="closeLocalFlightModal()">&times;</button> <button class="close" onclick="closeModal('localFlightModal')">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="local-flight-form"> <form id="local-flight-form">
@@ -475,7 +475,7 @@
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-info" onclick="closeLocalFlightModal()"> <button type="button" class="btn btn-info" onclick="closeModal('localFlightModal')">
Close Close
</button> </button>
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">
@@ -586,7 +586,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2>Book In</h2> <h2>Book In</h2>
<button class="close" onclick="closeBookInModal()">&times;</button> <button class="close" onclick="closeModal('bookInModal')">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="book-in-form"> <form id="book-in-form">
@@ -628,7 +628,7 @@
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-info" onclick="closeBookInModal()"> <button type="button" class="btn btn-info" onclick="closeModal('bookInModal')">
Close Close
</button> </button>
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">
@@ -645,7 +645,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2>Register Overflight</h2> <h2>Register Overflight</h2>
<button class="close" onclick="closeOverflightModal()">&times;</button> <button class="close" onclick="closeModal('overflightModal')">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="overflight-form"> <form id="overflight-form">
@@ -686,7 +686,7 @@
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-info" onclick="closeOverflightModal()"> <button type="button" class="btn btn-info" onclick="closeModal('overflightModal')">
Close Close
</button> </button>
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">
@@ -703,7 +703,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2 id="overflight-edit-title">Overflight Details</h2> <h2 id="overflight-edit-title">Overflight Details</h2>
<button class="close" onclick="closeOverflightEditModal()">&times;</button> <button class="close" onclick="closeModal('overflightEditModal')">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="quick-actions"> <div class="quick-actions">
@@ -758,7 +758,7 @@
</div> </div>
<div class="form-actions"> <div class="form-actions">
<button type="button" class="btn btn-info" onclick="closeOverflightEditModal()"> <button type="button" class="btn btn-info" onclick="closeModal('overflightEditModal')">
Close Close
</button> </button>
<button type="submit" class="btn btn-success"> <button type="submit" class="btn btn-success">
@@ -914,13 +914,13 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2>Table Information</h2> <h2>Table Information</h2>
<button class="close" onclick="closeTableHelp()">&times;</button> <button class="close" onclick="closeModal('tableHelpModal')">&times;</button>
</div> </div>
<div class="modal-body" id="tableHelpContent"> <div class="modal-body" id="tableHelpContent">
<!-- Content will be populated by JavaScript --> <!-- Content will be populated by JavaScript -->
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn btn-info" onclick="closeTableHelp()">Close</button> <button class="btn btn-info" onclick="closeModal('tableHelpModal')">Close</button>
</div> </div>
</div> </div>
</div> </div>
@@ -930,7 +930,7 @@
<div class="modal-content"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<h2>User Management</h2> <h2>User Management</h2>
<button class="close" onclick="closeUserManagementModal()">&times;</button> <button class="close" onclick="closeModal('userManagementModal')">&times;</button>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div class="quick-actions" style="margin-bottom: 1rem;"> <div class="quick-actions" style="margin-bottom: 1rem;">
@@ -1115,6 +1115,28 @@
let etdManuallyEdited = false; // Track if user has manually edited ETD let etdManuallyEdited = false; // Track if user has manually edited ETD
let loadPPRsTimeout = null; // Debounce timer for loadPPRs to prevent duplicate refreshes let loadPPRsTimeout = null; // Debounce timer for loadPPRs to prevent duplicate refreshes
// Modal state variables
let currentLocalFlightId = null;
let currentBookedInArrivalId = null;
let currentDepartureId = null;
let currentArrivalId = null;
let currentOverflightId = null;
let isOverflightQSYMode = false; // Track if we're in overflight QSY mode
// User management variables
let currentUserRole = null;
let isNewUser = false;
let currentUserId = null;
let currentChangePasswordUserId = null;
// ==================== GENERIC MODAL HELPER ====================
function closeModal(modalId, additionalCleanup = null) {
document.getElementById(modalId).style.display = 'none';
if (additionalCleanup) {
additionalCleanup();
}
}
// WebSocket connection for real-time updates // WebSocket connection for real-time updates
function connectWebSocket() { function connectWebSocket() {
if (wsConnection && wsConnection.readyState === WebSocket.OPEN) { if (wsConnection && wsConnection.readyState === WebSocket.OPEN) {
@@ -1130,11 +1152,9 @@
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/tower-updates`; const wsUrl = `${protocol}//${window.location.host}/ws/tower-updates`;
console.log('Connecting to WebSocket:', wsUrl);
wsConnection = new WebSocket(wsUrl); wsConnection = new WebSocket(wsUrl);
wsConnection.onopen = function(event) { wsConnection.onopen = function(event) {
console.log('✅ WebSocket connected for real-time updates');
lastHeartbeatResponse = Date.now(); lastHeartbeatResponse = Date.now();
startHeartbeat(); startHeartbeat();
showNotification('Real-time updates connected'); showNotification('Real-time updates connected');
@@ -1145,37 +1165,31 @@
// Check if it's a heartbeat response // Check if it's a heartbeat response
if (event.data.startsWith('Heartbeat:')) { if (event.data.startsWith('Heartbeat:')) {
lastHeartbeatResponse = Date.now(); lastHeartbeatResponse = Date.now();
console.log('💓 WebSocket heartbeat received');
return; return;
} }
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
console.log('WebSocket message received:', data);
// Refresh PPRs when any PPR-related event occurs // Refresh PPRs when any PPR-related event occurs
if (data.type && (data.type.includes('ppr_') || data.type === 'status_update')) { if (data.type && (data.type.includes('ppr_') || data.type === 'status_update')) {
console.log('PPR update detected, refreshing...');
loadPPRs(); loadPPRs();
showNotification('Data updated'); showNotification('Data updated');
} }
// Refresh local flights when any local flight event occurs // Refresh local flights when any local flight event occurs
if (data.type && (data.type.includes('local_flight_'))) { if (data.type && (data.type.includes('local_flight_'))) {
console.log('Local flight update detected, refreshing...');
loadPPRs(); loadPPRs();
showNotification('Local flight updated'); showNotification('Local flight updated');
} }
// Refresh departures when any departure event occurs // Refresh departures when any departure event occurs
if (data.type && (data.type.includes('departure_'))) { if (data.type && (data.type.includes('departure_'))) {
console.log('Departure update detected, refreshing...');
loadDepartures(); loadDepartures();
showNotification('Departure updated'); showNotification('Departure updated');
} }
// Refresh arrivals when any arrival event occurs // Refresh arrivals when any arrival event occurs
if (data.type && (data.type.includes('arrival_'))) { if (data.type && (data.type.includes('arrival_'))) {
console.log('Arrival update detected, refreshing...');
loadArrivals(); loadArrivals();
showNotification('Arrival updated'); showNotification('Arrival updated');
} }
@@ -1185,14 +1199,12 @@
}; };
wsConnection.onclose = function(event) { wsConnection.onclose = function(event) {
console.log('⚠️ WebSocket disconnected', event.code, event.reason);
stopHeartbeat(); stopHeartbeat();
// Attempt to reconnect after 5 seconds if still logged in // Attempt to reconnect after 5 seconds if still logged in
if (accessToken) { if (accessToken) {
showNotification('Real-time updates disconnected, reconnecting...', true); showNotification('Real-time updates disconnected, reconnecting...', true);
wsReconnectTimeout = setTimeout(() => { wsReconnectTimeout = setTimeout(() => {
console.log('Attempting to reconnect WebSocket...');
connectWebSocket(); connectWebSocket();
}, 5000); }, 5000);
} }
@@ -1211,7 +1223,6 @@
wsHeartbeatInterval = setInterval(() => { wsHeartbeatInterval = setInterval(() => {
if (wsConnection && wsConnection.readyState === WebSocket.OPEN) { if (wsConnection && wsConnection.readyState === WebSocket.OPEN) {
wsConnection.send('ping'); wsConnection.send('ping');
console.log('💓 Sending WebSocket heartbeat');
// Check if last heartbeat was more than 60 seconds ago // Check if last heartbeat was more than 60 seconds ago
if (lastHeartbeatResponse && (Date.now() - lastHeartbeatResponse > 60000)) { if (lastHeartbeatResponse && (Date.now() - lastHeartbeatResponse > 60000)) {
@@ -1388,21 +1399,21 @@
// Press 'Escape' to close Book Out modal if it's open (allow even when typing in inputs) // Press 'Escape' to close Book Out modal if it's open (allow even when typing in inputs)
if (e.key === 'Escape' && document.getElementById('localFlightModal').style.display === 'block') { if (e.key === 'Escape' && document.getElementById('localFlightModal').style.display === 'block') {
e.preventDefault(); e.preventDefault();
closeLocalFlightModal(); closeModal('localFlightModal');
return; return;
} }
// Press 'Escape' to close Book In modal if it's open (allow even when typing in inputs) // Press 'Escape' to close Book In modal if it's open (allow even when typing in inputs)
if (e.key === 'Escape' && document.getElementById('bookInModal').style.display === 'block') { if (e.key === 'Escape' && document.getElementById('bookInModal').style.display === 'block') {
e.preventDefault(); e.preventDefault();
closeBookInModal(); closeModal('bookInModal');
return; return;
} }
// Press 'Escape' to close Overflight modal if it's open (allow even when typing in inputs) // Press 'Escape' to close Overflight modal if it's open (allow even when typing in inputs)
if (e.key === 'Escape' && document.getElementById('overflightModal').style.display === 'block') { if (e.key === 'Escape' && document.getElementById('overflightModal').style.display === 'block') {
e.preventDefault(); e.preventDefault();
closeOverflightModal(); closeModal('overflightModal');
return; return;
} }
@@ -1430,7 +1441,7 @@
// Press 'Escape' to close overflight edit modal if it's open (allow even when typing in inputs) // Press 'Escape' to close overflight edit modal if it's open (allow even when typing in inputs)
if (e.key === 'Escape' && document.getElementById('overflightEditModal').style.display === 'block') { if (e.key === 'Escape' && document.getElementById('overflightEditModal').style.display === 'block') {
e.preventDefault(); e.preventDefault();
closeOverflightEditModal(); closeModal('overflightEditModal');
return; return;
} }
@@ -2714,11 +2725,9 @@
} }
function populateForm(ppr) { function populateForm(ppr) {
console.log('populateForm called with:', ppr);
Object.keys(ppr).forEach(key => { Object.keys(ppr).forEach(key => {
if (key === 'eta' || key === 'etd') { if (key === 'eta' || key === 'etd') {
if (ppr[key]) { if (ppr[key]) {
console.log(`Processing ${key}:`, ppr[key]);
// ppr[key] is UTC datetime string from API (naive, assume UTC) // ppr[key] is UTC datetime string from API (naive, assume UTC)
let utcDateStr = ppr[key]; let utcDateStr = ppr[key];
if (!utcDateStr.includes('T')) { if (!utcDateStr.includes('T')) {
@@ -2728,7 +2737,6 @@
utcDateStr += 'Z'; utcDateStr += 'Z';
} }
const date = new Date(utcDateStr); // Now correctly parsed as UTC const date = new Date(utcDateStr); // Now correctly parsed as UTC
console.log(`Parsed date for ${key}:`, date);
// Split into date and time components for separate inputs // Split into date and time components for separate inputs
const dateField = document.getElementById(`${key}-date`); const dateField = document.getElementById(`${key}-date`);
@@ -2741,7 +2749,6 @@
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const dateValue = `${year}-${month}-${day}`; const dateValue = `${year}-${month}-${day}`;
dateField.value = dateValue; dateField.value = dateValue;
console.log(`Set ${key}-date to:`, dateValue);
// Format time (round to nearest 15-minute interval) // Format time (round to nearest 15-minute interval)
const hours = String(date.getHours()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0');
@@ -2750,19 +2757,12 @@
const minutes = String(roundedMinutes).padStart(2, '0'); const minutes = String(roundedMinutes).padStart(2, '0');
const timeValue = `${hours}:${minutes}`; const timeValue = `${hours}:${minutes}`;
timeField.value = timeValue; timeField.value = timeValue;
console.log(`Set ${key}-time to:`, timeValue, `(from ${rawMinutes} minutes)`);
} else {
console.log(`Date/time fields not found for ${key}: dateField=${dateField}, timeField=${timeField}`);
} }
} else {
console.log(`${key} is empty`);
} }
} else { } else {
const field = document.getElementById(key); const field = document.getElementById(key);
if (field) { if (field) {
field.value = ppr[key] || ''; field.value = ppr[key] || '';
} else {
console.log(`Field not found for key: ${key}`);
} }
} }
}); });
@@ -2838,9 +2838,10 @@
} }
function closePPRModal() { function closePPRModal() {
document.getElementById('pprModal').style.display = 'none'; closeModal('pprModal', () => {
currentPPRId = null; currentPPRId = null;
isNewPPR = false; isNewPPR = false;
});
} }
// Timestamp modal functions // Timestamp modal functions
@@ -3181,10 +3182,6 @@
} }
// User Management Functions // User Management Functions
let currentUserRole = null;
let isNewUser = false;
let currentUserId = null;
async function openUserManagementModal() { async function openUserManagementModal() {
if (!accessToken) return; if (!accessToken) return;
@@ -3192,10 +3189,6 @@
await loadUsers(); await loadUsers();
} }
function closeUserManagementModal() {
document.getElementById('userManagementModal').style.display = 'none';
}
async function loadUsers() { async function loadUsers() {
if (!accessToken) return; if (!accessToken) return;
@@ -3325,13 +3318,12 @@
} }
function closeUserModal() { function closeUserModal() {
document.getElementById('userModal').style.display = 'none'; closeModal('userModal', () => {
currentUserId = null; currentUserId = null;
isNewUser = false; isNewUser = false;
});
} }
let currentChangePasswordUserId = null;
function openChangePasswordModal(userId, username) { function openChangePasswordModal(userId, username) {
if (!accessToken) return; if (!accessToken) return;
@@ -3348,8 +3340,9 @@
} }
function closeChangePasswordModal() { function closeChangePasswordModal() {
document.getElementById('changePasswordModal').style.display = 'none'; closeModal('changePasswordModal', () => {
currentChangePasswordUserId = null; currentChangePasswordUserId = null;
});
} }
// Change password form submission // Change password form submission
@@ -3450,9 +3443,7 @@
// Update user role detection and UI visibility // Update user role detection and UI visibility
async function updateUserRole() { async function updateUserRole() {
console.log('updateUserRole called'); // Debug log
if (!accessToken) { if (!accessToken) {
console.log('No access token, skipping role update'); // Debug log
return; return;
} }
@@ -3464,16 +3455,13 @@
if (response.ok) { if (response.ok) {
const userData = await response.json(); const userData = await response.json();
currentUserRole = userData.role; currentUserRole = userData.role;
console.log('User role from API:', currentUserRole); // Debug log
// Show user management in dropdown only for administrators // Show user management in dropdown only for administrators
const userManagementDropdown = document.getElementById('user-management-dropdown'); const userManagementDropdown = document.getElementById('user-management-dropdown');
if (currentUserRole && currentUserRole.toUpperCase() === 'ADMINISTRATOR') { if (currentUserRole && currentUserRole.toUpperCase() === 'ADMINISTRATOR') {
userManagementDropdown.style.display = 'block'; userManagementDropdown.style.display = 'block';
console.log('Showing user management in dropdown'); // Debug log
} else { } else {
userManagementDropdown.style.display = 'none'; userManagementDropdown.style.display = 'none';
console.log('Hiding user management, current role:', currentUserRole); // Debug log
} }
} }
} catch (error) { } catch (error) {
@@ -3499,16 +3487,16 @@
closeTimestampModal(); closeTimestampModal();
} }
if (event.target === userManagementModal) { if (event.target === userManagementModal) {
closeUserManagementModal(); closeModal('userManagementModal');
} }
if (event.target === userModal) { if (event.target === userModal) {
closeUserModal(); closeUserModal();
} }
if (event.target === tableHelpModal) { if (event.target === tableHelpModal) {
closeTableHelp(); closeModal('tableHelpModal');
} }
if (event.target === bookInModal) { if (event.target === bookInModal) {
closeBookInModal(); closeModal('bookInModal');
} }
} }
@@ -3547,10 +3535,6 @@
}, 100); }, 100);
} }
function closeLocalFlightModal() {
document.getElementById('localFlightModal').style.display = 'none';
}
function openBookInModal() { function openBookInModal() {
document.getElementById('book-in-form').reset(); document.getElementById('book-in-form').reset();
document.getElementById('book-in-id').value = ''; document.getElementById('book-in-id').value = '';
@@ -3569,10 +3553,6 @@
}, 100); }, 100);
} }
function closeBookInModal() {
document.getElementById('bookInModal').style.display = 'none';
}
function openOverflightModal() { function openOverflightModal() {
document.getElementById('overflight-form').reset(); document.getElementById('overflight-form').reset();
document.getElementById('overflight-id').value = ''; document.getElementById('overflight-id').value = '';
@@ -3598,13 +3578,6 @@
}, 100); }, 100);
} }
function closeOverflightModal() {
document.getElementById('overflightModal').style.display = 'none';
}
let currentOverflightId = null;
let isOverflightQSYMode = false; // Track if we're in overflight QSY mode
async function openOverflightEditModal(overflightId) { async function openOverflightEditModal(overflightId) {
if (!accessToken) return; if (!accessToken) return;
@@ -3658,8 +3631,7 @@
} }
function closeOverflightEditModal() { function closeOverflightEditModal() {
document.getElementById('overflightEditModal').style.display = 'none'; closeModal('overflightEditModal');
currentOverflightId = null;
} }
async function updateOverflightStatus(newStatus, qsyTime = null) { async function updateOverflightStatus(newStatus, qsyTime = null) {
@@ -3941,9 +3913,6 @@
} }
// Local Flight Edit Modal Functions // Local Flight Edit Modal Functions
let currentLocalFlightId = null;
let currentBookedInArrivalId = null;
async function openLocalFlightEditModal(flightId) { async function openLocalFlightEditModal(flightId) {
if (!accessToken) return; if (!accessToken) return;
@@ -4092,8 +4061,9 @@
} }
function closeDepartureEditModal() { function closeDepartureEditModal() {
document.getElementById('departureEditModal').style.display = 'none'; closeModal('departureEditModal', () => {
currentDepartureId = null; currentDepartureId = null;
});
} }
// Departure edit form submission // Departure edit form submission
@@ -4150,8 +4120,6 @@
}); });
// Arrival Edit Modal Functions // Arrival Edit Modal Functions
let currentArrivalId = null;
async function openArrivalEditModal(arrivalId) { async function openArrivalEditModal(arrivalId) {
if (!accessToken) return; if (!accessToken) return;
@@ -4196,8 +4164,9 @@
} }
function closeArrivalEditModal() { function closeArrivalEditModal() {
document.getElementById('arrivalEditModal').style.display = 'none'; closeModal('arrivalEditModal', () => {
currentArrivalId = null; currentArrivalId = null;
});
} }
// Arrival edit form submission // Arrival edit form submission
@@ -4457,10 +4426,6 @@
modal.style.display = 'block'; modal.style.display = 'block';
} }
function closeTableHelp() {
document.getElementById('tableHelpModal').style.display = 'none';
}
// Local flight edit form submission // Local flight edit form submission
document.getElementById('local-flight-edit-form').addEventListener('submit', async function(e) { document.getElementById('local-flight-edit-form').addEventListener('submit', async function(e) {
e.preventDefault(); e.preventDefault();
@@ -4563,8 +4528,6 @@
delete flightData.flight_type; delete flightData.flight_type;
} }
console.log(`Submitting ${endpoint} data:`, flightData);
try { try {
const response = await fetch(endpoint, { const response = await fetch(endpoint, {
method: 'POST', method: 'POST',
@@ -4593,7 +4556,7 @@
} }
const result = await response.json(); const result = await response.json();
closeLocalFlightModal(); closeModal('localFlightModal');
loadPPRs(); // Refresh tables loadPPRs(); // Refresh tables
showNotification(`Aircraft ${result.registration} booked out successfully!`); showNotification(`Aircraft ${result.registration} booked out successfully!`);
} catch (error) { } catch (error) {
@@ -4643,8 +4606,6 @@
// Book In uses LANDED status (they're arriving now) // Book In uses LANDED status (they're arriving now)
arrivalData.status = 'LANDED'; arrivalData.status = 'LANDED';
console.log('Submitting arrivals data:', arrivalData);
try { try {
const response = await fetch('/api/v1/arrivals/', { const response = await fetch('/api/v1/arrivals/', {
method: 'POST', method: 'POST',
@@ -4673,7 +4634,7 @@
} }
const result = await response.json(); const result = await response.json();
closeBookInModal(); closeModal('bookInModal');
loadPPRs(); // Refresh tables loadPPRs(); // Refresh tables
showNotification(`Aircraft ${result.registration} booked in successfully!`); showNotification(`Aircraft ${result.registration} booked in successfully!`);
} catch (error) { } catch (error) {
@@ -4710,8 +4671,6 @@
} }
}); });
console.log('Submitting overflight data:', overflightData);
try { try {
const response = await fetch('/api/v1/overflights/', { const response = await fetch('/api/v1/overflights/', {
method: 'POST', method: 'POST',
@@ -4740,7 +4699,7 @@
} }
const result = await response.json(); const result = await response.json();
closeOverflightModal(); closeModal('overflightModal');
loadPPRs(); loadPPRs();
showNotification(`Overflight ${result.registration} registered successfully!`); showNotification(`Overflight ${result.registration} registered successfully!`);
} catch (error) { } catch (error) {
@@ -4802,6 +4761,7 @@
document.getElementById('etd-date').addEventListener('change', markETDAsManuallyEdited); document.getElementById('etd-date').addEventListener('change', markETDAsManuallyEdited);
document.getElementById('etd-time').addEventListener('change', markETDAsManuallyEdited); document.getElementById('etd-time').addEventListener('change', markETDAsManuallyEdited);
}); });
</script> </script>
<!-- Footer Bar --> <!-- Footer Bar -->