local-flights #5
105
web/admin.html
105
web/admin.html
@@ -942,6 +942,7 @@
|
|||||||
let sessionExpiryWarningShown = false;
|
let sessionExpiryWarningShown = false;
|
||||||
let sessionExpiryCheckInterval = null;
|
let sessionExpiryCheckInterval = null;
|
||||||
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
|
||||||
|
|
||||||
// WebSocket connection for real-time updates
|
// WebSocket connection for real-time updates
|
||||||
function connectWebSocket() {
|
function connectWebSocket() {
|
||||||
@@ -1406,8 +1407,17 @@
|
|||||||
async function loadPPRs() {
|
async function loadPPRs() {
|
||||||
if (!accessToken) return;
|
if (!accessToken) return;
|
||||||
|
|
||||||
|
// Debounce: prevent duplicate refreshes if called multiple times in quick succession
|
||||||
|
// (e.g., from form submission + WebSocket update at the same time)
|
||||||
|
if (loadPPRsTimeout) {
|
||||||
|
clearTimeout(loadPPRsTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadPPRsTimeout = setTimeout(async () => {
|
||||||
// Load all tables simultaneously
|
// Load all tables simultaneously
|
||||||
await Promise.all([loadArrivals(), loadDepartures(), loadDeparted(), loadParked(), loadUpcoming()]);
|
await Promise.all([loadArrivals(), loadDepartures(), loadDeparted(), loadParked(), loadUpcoming()]);
|
||||||
|
loadPPRsTimeout = null;
|
||||||
|
}, 100); // Wait 100ms before executing to batch multiple calls
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load arrivals (NEW and CONFIRMED status for PPR, DEPARTED for local flights)
|
// Load arrivals (NEW and CONFIRMED status for PPR, DEPARTED for local flights)
|
||||||
@@ -1629,26 +1639,15 @@
|
|||||||
function displayDeparted(departed) {
|
function displayDeparted(departed) {
|
||||||
const tbody = document.getElementById('departed-table-body');
|
const tbody = document.getElementById('departed-table-body');
|
||||||
|
|
||||||
// Deduplicate departed flights by ID to prevent duplicates from race conditions
|
document.getElementById('departed-count').textContent = departed.length;
|
||||||
const seenIds = new Set();
|
|
||||||
const uniqueDeparted = departed.filter(flight => {
|
|
||||||
if (seenIds.has(flight.id)) {
|
|
||||||
console.warn(`Duplicate departed flight detected and filtered: ID ${flight.id}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds.add(flight.id);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('departed-count').textContent = uniqueDeparted.length;
|
if (departed.length === 0) {
|
||||||
|
|
||||||
if (uniqueDeparted.length === 0) {
|
|
||||||
document.getElementById('departed-no-data').style.display = 'block';
|
document.getElementById('departed-no-data').style.display = 'block';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by departed time
|
// Sort by departed time
|
||||||
uniqueDeparted.sort((a, b) => {
|
departed.sort((a, b) => {
|
||||||
const aTime = a.departed_dt;
|
const aTime = a.departed_dt;
|
||||||
const bTime = b.departed_dt;
|
const bTime = b.departed_dt;
|
||||||
return new Date(aTime) - new Date(bTime);
|
return new Date(aTime) - new Date(bTime);
|
||||||
@@ -1657,7 +1656,7 @@
|
|||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
document.getElementById('departed-table-content').style.display = 'block';
|
document.getElementById('departed-table-content').style.display = 'block';
|
||||||
|
|
||||||
for (const flight of uniqueDeparted) {
|
for (const flight of departed) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
const isLocal = flight.isLocalFlight;
|
const isLocal = flight.isLocalFlight;
|
||||||
const isDeparture = flight.isDeparture;
|
const isDeparture = flight.isDeparture;
|
||||||
@@ -1745,26 +1744,15 @@
|
|||||||
function displayParked(parked) {
|
function displayParked(parked) {
|
||||||
const tbody = document.getElementById('parked-table-body');
|
const tbody = document.getElementById('parked-table-body');
|
||||||
|
|
||||||
// Deduplicate parked flights by ID to prevent duplicates from race conditions
|
document.getElementById('parked-count').textContent = parked.length;
|
||||||
const seenIds = new Set();
|
|
||||||
const uniqueParked = parked.filter(flight => {
|
|
||||||
if (seenIds.has(flight.id)) {
|
|
||||||
console.warn(`Duplicate parked flight detected and filtered: ID ${flight.id}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds.add(flight.id);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('parked-count').textContent = uniqueParked.length;
|
if (parked.length === 0) {
|
||||||
|
|
||||||
if (uniqueParked.length === 0) {
|
|
||||||
document.getElementById('parked-no-data').style.display = 'block';
|
document.getElementById('parked-no-data').style.display = 'block';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by landed time
|
// Sort by landed time
|
||||||
uniqueParked.sort((a, b) => {
|
parked.sort((a, b) => {
|
||||||
if (!a.landed_dt) return 1;
|
if (!a.landed_dt) return 1;
|
||||||
if (!b.landed_dt) return -1;
|
if (!b.landed_dt) return -1;
|
||||||
return new Date(a.landed_dt) - new Date(b.landed_dt);
|
return new Date(a.landed_dt) - new Date(b.landed_dt);
|
||||||
@@ -1773,7 +1761,7 @@
|
|||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
document.getElementById('parked-table-content').style.display = 'block';
|
document.getElementById('parked-table-content').style.display = 'block';
|
||||||
|
|
||||||
for (const ppr of uniqueParked) {
|
for (const ppr of parked) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
|
|
||||||
// All rows are PPR, so make them clickable
|
// All rows are PPR, so make them clickable
|
||||||
@@ -1863,31 +1851,20 @@
|
|||||||
function displayUpcoming(upcoming) {
|
function displayUpcoming(upcoming) {
|
||||||
const tbody = document.getElementById('upcoming-table-body');
|
const tbody = document.getElementById('upcoming-table-body');
|
||||||
|
|
||||||
// Deduplicate upcoming flights by ID to prevent duplicates from race conditions
|
document.getElementById('upcoming-count').textContent = upcoming.length;
|
||||||
const seenIds = new Set();
|
|
||||||
const uniqueUpcoming = upcoming.filter(ppr => {
|
|
||||||
if (seenIds.has(ppr.id)) {
|
|
||||||
console.warn(`Duplicate upcoming flight detected and filtered: ID ${ppr.id}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds.add(ppr.id);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
document.getElementById('upcoming-count').textContent = uniqueUpcoming.length;
|
if (upcoming.length === 0) {
|
||||||
|
|
||||||
if (uniqueUpcoming.length === 0) {
|
|
||||||
// Don't show anything if collapsed by default
|
// Don't show anything if collapsed by default
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by ETA date and time
|
// Sort by ETA date and time
|
||||||
uniqueUpcoming.sort((a, b) => new Date(a.eta) - new Date(b.eta));
|
upcoming.sort((a, b) => new Date(a.eta) - new Date(b.eta));
|
||||||
|
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
// Don't auto-expand, keep collapsed by default
|
// Don't auto-expand, keep collapsed by default
|
||||||
|
|
||||||
for (const ppr of uniqueUpcoming) {
|
for (const ppr of upcoming) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
row.onclick = () => openPPRModal(ppr.id);
|
row.onclick = () => openPPRModal(ppr.id);
|
||||||
row.style.cssText = 'font-size: 0.85rem !important;';
|
row.style.cssText = 'font-size: 0.85rem !important;';
|
||||||
@@ -1966,25 +1943,14 @@
|
|||||||
const tbody = document.getElementById('arrivals-table-body');
|
const tbody = document.getElementById('arrivals-table-body');
|
||||||
const recordCount = document.getElementById('arrivals-count');
|
const recordCount = document.getElementById('arrivals-count');
|
||||||
|
|
||||||
// Deduplicate arrivals by ID to prevent duplicates from race conditions
|
recordCount.textContent = arrivals.length;
|
||||||
const seenIds = new Set();
|
if (arrivals.length === 0) {
|
||||||
const uniqueArrivals = arrivals.filter(flight => {
|
|
||||||
if (seenIds.has(flight.id)) {
|
|
||||||
console.warn(`Duplicate arrival detected and filtered: ID ${flight.id}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds.add(flight.id);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
recordCount.textContent = uniqueArrivals.length;
|
|
||||||
if (uniqueArrivals.length === 0) {
|
|
||||||
document.getElementById('arrivals-no-data').style.display = 'block';
|
document.getElementById('arrivals-no-data').style.display = 'block';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort arrivals by ETA/departure time (ascending)
|
// Sort arrivals by ETA/departure time (ascending)
|
||||||
uniqueArrivals.sort((a, b) => {
|
arrivals.sort((a, b) => {
|
||||||
const aTime = a.eta || a.departure_dt;
|
const aTime = a.eta || a.departure_dt;
|
||||||
const bTime = b.eta || b.departure_dt;
|
const bTime = b.eta || b.departure_dt;
|
||||||
if (!aTime) return 1;
|
if (!aTime) return 1;
|
||||||
@@ -1993,7 +1959,7 @@
|
|||||||
});
|
});
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
document.getElementById('arrivals-table-content').style.display = 'block';
|
document.getElementById('arrivals-table-content').style.display = 'block';
|
||||||
for (const flight of uniqueArrivals) {
|
for (const flight of arrivals) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
const isLocal = flight.isLocalFlight;
|
const isLocal = flight.isLocalFlight;
|
||||||
const isBookedIn = flight.isBookedIn;
|
const isBookedIn = flight.isBookedIn;
|
||||||
@@ -2137,25 +2103,14 @@
|
|||||||
const tbody = document.getElementById('departures-table-body');
|
const tbody = document.getElementById('departures-table-body');
|
||||||
const recordCount = document.getElementById('departures-count');
|
const recordCount = document.getElementById('departures-count');
|
||||||
|
|
||||||
// Deduplicate departures by ID to prevent duplicates from race conditions
|
recordCount.textContent = departures.length;
|
||||||
const seenIds = new Set();
|
if (departures.length === 0) {
|
||||||
const uniqueDepartures = departures.filter(flight => {
|
|
||||||
if (seenIds.has(flight.id)) {
|
|
||||||
console.warn(`Duplicate flight detected and filtered: ID ${flight.id}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
seenIds.add(flight.id);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
recordCount.textContent = uniqueDepartures.length;
|
|
||||||
if (uniqueDepartures.length === 0) {
|
|
||||||
document.getElementById('departures-no-data').style.display = 'block';
|
document.getElementById('departures-no-data').style.display = 'block';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort departures by ETD (ascending), nulls last
|
// Sort departures by ETD (ascending), nulls last
|
||||||
uniqueDepartures.sort((a, b) => {
|
departures.sort((a, b) => {
|
||||||
const aTime = a.etd || a.created_dt;
|
const aTime = a.etd || a.created_dt;
|
||||||
const bTime = b.etd || b.created_dt;
|
const bTime = b.etd || b.created_dt;
|
||||||
if (!aTime) return 1;
|
if (!aTime) return 1;
|
||||||
@@ -2164,7 +2119,7 @@
|
|||||||
});
|
});
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
document.getElementById('departures-table-content').style.display = 'block';
|
document.getElementById('departures-table-content').style.display = 'block';
|
||||||
for (const flight of uniqueDepartures) {
|
for (const flight of departures) {
|
||||||
const row = document.createElement('tr');
|
const row = document.createElement('tr');
|
||||||
const isLocal = flight.isLocalFlight;
|
const isLocal = flight.isLocalFlight;
|
||||||
const isDeparture = flight.isDeparture;
|
const isDeparture = flight.isDeparture;
|
||||||
|
|||||||
Reference in New Issue
Block a user